go语言离线 go语言最新版本

golangci-lint使用笔记GolangCI-Lint是一个lint聚合器,它的速度很快,平均速度是gometalinter的5倍 。它易于集成和使用,具有良好的输出并且具有最小数量的误报 。而且它还支持go modules 。最重要的是免费开源 。
方式一:使用命令: go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
方式二:离线安装:
可以通过命令golangci-lint help linters查看它支持的linters 。你可以传入参数-E/--enable来使某个linter可用,也可以使用-D/--disable参数来使某个linter不可用 。
光子网络(photon network)主网部署上线光子网络是一种适用于以太坊和光谱区块链上ERC20 token和ERC223 token的链下支付网络go语言离线,具有快速、可扩展和低成本的特点,并提供了与区块链类似的安全性和去中心化保证 。在兼容性方面,光子网络不仅可以在windows、Linux、macOS等平台上运行,而且支持Android、iOS等移动平台go语言离线;为适配移动平台安全性和稳定性需求,光子网络采用精简状态恢复、异步响应及Matrix集群通信机制,提升了移动微支付的用户体验 。更进一步,在光谱生态meshbox的支持下,光子网络还可以进行更有效的链接支付以及直接通道无网支付,解决网络信号不佳及网络拥堵等场景下的支付需求 。最后,光子网络还提供了跨链token 原子互换以及较完善的第三方服务支持,包括go语言离线:与比特币、莱特币、以太坊、SMT的两两跨链互换,第三方代理服务帮助离线节点监控通道资金安全 , 第三方路由查找服务提供路由查询及通道收费等 , 光子网络将与光谱生态一起构建价值传输的桥梁 , 为真正万物互联奠定坚实的基础 。
2.1 基础功能
光子网络的基础功能包括:通道打开、通道存款、链下直接通道转账、链下间接通道转账、通道关闭、通道更新、通道结算 。简要描述基础功能使用场景如下:假设有三个用户Alice、 Bob和Charlie想使用光子网络进行链下转账交易 。以Alice发给Bob、Alice发送给Chalie为例:Alice打算转账30个token给Bob,转账20token给Charlie 。简单支付过程会是什么样呢go语言离线?
第一步,打开通道 。如果Alice和Bob是第一次使用photon network进行转账交易 , 他们之前没有直接通道相连 。那么对于交易发起者Alice来说, 她需要先使用打开通道功能 , 在Alice和Bob之间建立一条通道 。此时,双方建立的通道内没有token,Alice和Bob可以在通道建立后,单独进行存款操作,也可以在建立通道的同时,存一部分token进通道内 。完成了打开通道,就可以使用新建的通道 。同理 , Alice也可以与Charlie打开通道,Bob也可以与Charlie打开通道,我们假设是Bob与Charlie打开了通道 。
第二步,存款进通道 。如果Alice在第一步通道创建时没有进行存款,为了能够给Bob进行转账,则Alice需要使用通道存款功能,向新创建的通道存入一定数量的token,存款操作可以重复进行多次,Alice和Bob都可以向通道内存款 。假定现在Alice向Alice—— Bob通道存了100 token进通道,则目前Alice——Bob通道总容量为100 token,其中Alice为100 token,Bob为0 token 。此外,Bob在Bob——Charlie通道内也存入80 token 。
第三步,链下转账 。在光子网络的支持下,Alice可以向Bob发送30token的转账,因为目前Alice和Bob之间存在直接通道,并且在通道内Alice有100 token的余额 , 因此 , 直接通道转账成功 。转账完成后 , Alice的存款为70token,Bob的存款为30token,此时 , 因为交易成功发送,没有锁定token 。Alice向Charlie发送20token的转账,因为Alice和Charlie之间没有直接通道 , 所以不能进行直接通道链下转账go语言离线;但是,Alice和Bob,Bob和Charlie之间有直接通道,Alice可以通过Bob向Charlie进行间接通道转账 。转账完成后,Alice—— Bob通道内余额为Alice 50token,Bob 50token; Bob—— Charlie通道内余额为Bob 60token,Charlie 20token 。
【go语言离线 go语言最新版本】第四步:关闭通道 。Alice在与Bob交易完成后 , 不想再继续使用她们之间的这条通道,想取回通道内的资金 , 此时Alice可以使用关闭通道功能单方面关闭这条通道,并等待通道更新后进行结算 。
第五步:更新通道 。Alice为了不损失钱,在通道关闭的同时需要提交最新的余额证明 , 更新对方给自己的转账金额;同样,Bob得知Alice打算关闭他们之间的通道,为了防止自己受损失 , Bob也需要使用更新通道功能,提交Alice转账给自己的证明,双方更新通道余额(并解锁已注册锁的交易)后,可以进行通道结算 。
第六步:结算通道 。双方在结算窗口期后,任意一方可以使用通道结算功能将通道内的资金返还回双方各自的链上帐户中 。如Alice 50token ,Bob 50token 。结算完成后 , Alice——Bob这条通道将被销毁 。
2.2 特色功能
在基础功能之外 , 光子网络还有以下一些特色功能 。
(1)合作关闭通道
在大部分的情况下,通道的双方是合作的 。因此,单方关闭通道等待超时增加了复杂度和花费 。为了提高效率,光子网络增加了合作关闭通道功能,通道双方只需要签名表示同意最终状态 。双方合作关闭通道,可以立即进行结算,顺利情况下可以在20秒内,将 token 返回到各自账户上 。
(2)不关闭通道取现
通道参与双方通过协商一致,在不关闭通道的情况下,可以从通道中提取一部分资金到自己的链上账户 。光子网络为了防止重放攻击,在取钱后重新设置通道打开的区块数,防止取钱后一方使用旧的余额证明关闭通道获取额外利益 。
(3)更完善的第三方支持
光子网络提供第三方代理服务,支持代理提交余额证明,代理解锁等功能,并可延伸支持代理创建通道、代理存款等功能,通道参与双方可以在没有光谱(以太坊)的情况下(离线)进行创建通道等操作 , 并且保证自身利益的安全;光子网络提供第三方路由查找服务,优化路由计算效率和准确率以提高转账成功率,同时实现通道收费功能,激励中间节点以提升光子网络整体使用效能 。
(4)引入惩罚机制
为了提高路由效率及资金的利用率,光子网络设计了交易声明放弃方式取代交易双方互锁方式,中转节点声明放弃余额不足的转账,从通道双方的锁集合中移除这个转账 , 由前向节点重选路由转发 。如果放弃锁的节点在结算前对已放弃的锁重新解锁,光子网络将对不诚实的路由中间节点进行惩罚 。
(5)密码注册
光子网络新增密码链上注册功能,通过链上密码注册时间判断交易是否过期 , 未完成的短期交易(锁超时)只要有密码(secret)已经注册即可认为安全,资金通道利用率更高,不会因为某个交易失败,而造成通道关闭 。
(6)支持 ERC223 token
ERC223 token标准在现有的ERC20标准基础引入了一些新功能,如防止意外转账的发生等 。目前已有部分token支持ERC223标准,为了使光子网络更具普遍应用价值 , 相应增加了对ERC223 token的支持 。
(7)支持 ERC20 扩展 ApproveAndCall
增加ApproveAndCall 接口可以对token逻辑和业务逻辑操作进行简化,光子网络为适应更多token的调用需要,已增加ApproveAndCall扩展,支持多种模式的调用 。
三、光子网络(photon network)优点
光子网络最主要的功能是进行链下微支付,具有安全、快速、可扩展和低费用的优点 。此外,光子网络与其他的状态通道相比,还具有以下一些优点:
(1)广泛的跨平台和移动适配应用
现有的一些状态通道技术实现多基于特定的平台,目前能够实现跨平台功能应用的相对较少,光子网络采用go语言开发,可以在windows、Linux、macOS等平台上运行 。另外 , 光子网络设计的应用场景之一是目前应用广泛的移动端和智能设备,因此可以完全适配Android和iOS等移动端系统 。光子网络为了提高移动设备的通信稳定性和更好的用户体验,使用Matrix消息框架进行底层通信,此集群管理方案在普通的硬件支持下可达每秒百万级别的吞吐量 , 其点对点通信在稳定可靠(均需签名和加密)的room内进行,可实时监控节点的状态并提供高效的通信效能 。
(2)精简的安全保护(状态恢复)机制
为了保证交易过程的正常进行 , 以及更好的适配移动设备,光子网络对交易中可能出现的意外场景进行了分析并针对性的设计了解决方案,实现交易节点状态数据的同步以保护交易安全 。为了避免交易过程中出现意外造成移动设备存储过多信息 , 光子网络没有采取所有情况都可以继续交易的机制(这样会存储大量数据,对移动设备的使用效率会有较大影响),在确保意外节点重启后不会丢失token,通道仍可以继续使用的原则上设计了精简状态恢复机制 , 整体上保护了交易的安全 。
(3)日趋完善的第三方支持
光子网络从安全和效率的角度提供了多个第三方支持,当前的第三方支持包括第三方代理和第三方路由服务,第三方代理可以帮助委托方监控通道状态,并在通道另一方关闭通道结算时更新通道状态,对移动节点离线后资金的安全有良好的保证;第三方路由服务接收当前所有节点的通道状态和收费信息 , 可实时监控节点的状态并提供当前最优的路由及收费方案来提高转账成功率 。目前,第三方服务正进一步完善性能并集成进光子网络,方便用户对光子网络的使用 。
(4)特殊应用场景支持
无网和跨链是光子网络的亮点 。在meshbox的支持下 , 光子网络可以在不依赖互联网(公链)的情况下进行直接通道无网安全支付,在网络信号不佳和网络拥堵的场景下有现实的应用需求;此外,光子网络可以在不借助第三方的情况下实现链下原子资产转换,对应应用广泛的跨链市场需求 。光子网络针对无网和跨链设计了相应的接口和使用流程 , 初步解决了用户在特殊场景下支付和交换难题 。
源码地址:
开发文档:
go是什么编程语言?主要应用于哪些方面?Go语言由Google公司开发,并于2009年开源,相比Java/Python/C等语言 , Go尤其擅长并发编程,性能堪比C语言,开发效率肩比Python , 被誉为“21世纪的C语言” 。
Go语言在云计算、大数据、微服务、高并发领域应用应用非常广泛 。BAT大厂正在把Go作为新项目开发的首选语言 。
Go语言能干什么?
1、服务端开发:以前你使用C或者C做的那些事情,用Go来做很合适,例如日志处理、文件系统、监控系统等;
2、DevOps:运维生态中的Docker、K8s、prometheus、grafana、open-falcon等都是使用Go语言开发;
3、网络编程:大量优秀的Web框架如Echo、Gin、Iris、beego等,而且Go内置的 net/http包十分的优秀;
4、Paas云平台领域:Kubernetes和Docker Swarm等;
5、分布式存储领域:etcd、Groupcache、TiDB、Cockroachdb、Influxdb等;
6、区块链领域:区块链里面有两个明星项目以太坊和fabric都使用Go语言;
7、容器虚拟化:大名鼎鼎的Docker就是使用Go语言实现的;
8、爬虫及大数据:Go语言天生支持并发,所以十分适合编写分布式爬虫及大数据处理 。
离线如何使用扩展程序运行离线使用扩展程序的方式可以通过以下几种方式:
1. 使用本地容器:使用本地容器可以在离线模式下运行扩展程序,可以使用Docker , Kubernetes或其他容器技术来实现 。
2. 使用本地应用程序:也可以使用本地应用程序来运行扩展程序,可以使用Node.js,Java , Go , Python等语言来实现这一功能 。
3. 使用本地Web服务器:也可以使用本地Web服务器(如Apache , Nginx , IIS)来运行扩展程序,这样可以让扩展程序可以在离线环境下运行 。
4. 使用本地代理:也可以使用本地代理来运行扩展程序,这样可以让扩展程序可以在离线环境下运行,而且可以保持代理的安全性 。
如何实现支持数亿用户的长连消息系统此文是根据周洋在【高可用架构群】中go语言离线的分享内容整理而成,转发请注明出处 。
周洋,360手机助手技术经理及架构师 , 负责360长连接消息系统,360手机助手架构的开发与维护 。
不知道咱们群名什么时候改为“Python高可用架构群”了,所以不得不说,很荣幸能在接下来的一个小时里在Python群里讨论golang....
360消息系统介绍
360消息系统更确切的说是长连接push系统,目前服务于360内部多个产品 , 开发平台数千款app,也支持部分聊天业务场景 , 单通道多app复用,支持上行数据,提供接入方不同粒度的上行数据和用户状态回调服务 。
目前整个系统按不同业务分成9个功能完整的集群 , 部署在多个idc上(每个集群覆盖不同的idc),实时在线数亿量级 。通常情况下,pc,手机 , 甚至是智能硬件上的360产品的push消息,基本上是从我们系统发出的 。
关于push系统对比与性能指标的讨论
很多同行比较关心go语言在实现push系统上的性能问题 , 单机性能究竟如何,能否和其go语言离线他语言实现的类似系统做对比么go语言离线?甚至问如果是创业,第三方云推送平台,推荐哪个?
其实各大厂都有类似的push系统,市场上也有类似功能的云服务 。包括我们公司早期也有erlang , nodejs实现的类似系统,也一度被公司要求做类似的对比测试 。我感觉在讨论对比数据的时候 , 很难保证大家环境和需求的统一,我只能说下我这里的体会,数据是有的,但这个数据前面估计会有很多定语~
第一个重要指标:单机的连接数指标
做过长连接的同行,应该有体会 , 如果在稳定连接情况下 , 连接数这个指标 , 在没有网络吞吐情况下对比,其实意义往往不大 , 维持连接消耗cpu资源很小,每条连接tcp协议栈会占约4k的内存开销,系统参数调整后,我们单机测试数据 , 最高也是可以达到单实例300w长连接 。但做更高的测试 , 我个人感觉意义不大 。
因为实际网络环境下,单实例300w长连接,从理论上算压力就很大:实际弱网络环境下,移动客户端的断线率很高,假设每秒有1000分之一的用户断线重连 。300w长连接,每秒新建连接达到3w,这同时连入的3w用户 , 要进行注册,加载离线存储等对内rpc调用,另外300w长连接的用户心跳需要维持,假设心跳300s一次,心跳包每秒需要1w tps 。单播和多播数据的转发,广播数据的转发 , 本身也要响应内部的rpc调用,300w长连接情况下,gc带来的压力 , 内部接口的响应延迟能否稳定保障 。这些集中在一个实例中,可用性是一个挑战 。所以线上单实例不会hold很高的长连接,实际情况也要根据接入客户端网络状况来决定 。
第二个重要指标:消息系统的内存使用量指标
这一点上,使用go语言情况下,由于协程的原因,会有一部分额外开销 。但是要做两个推送系统的对比,也有些需要确定问题 。比如系统从设计上是否需要全双工(即读写是否需要同时进行)如果半双工,理论上对一个用户的连接只需要使用一个协程即可(这种情况下,对用户的断线检测可能会有延时) , 如果是全双工,那读/写各一个协程 。两种场景内存开销是有区别的 。
另外测试数据的大小往往决定我们对连接上设置的读写buffer是多大,是全局复用的,还是每个连接上独享的,还是动态申请的 。另外是否全双工也决定buffer怎么开 。不同的策略,可能在不同情况的测试中表现不一样 。
第三个重要指标:每秒消息下发量
这一点上,也要看我们对消息到达的QoS级别(回复ack策略区别),另外看架构策略,每种策略有其更适用的场景,是纯粹推go语言离线?还是推拉结合?甚至是否开启了消息日志?日志库的实现机制、以及缓冲开多大?flush策略……这些都影响整个系统的吞吐量 。
另外为了HA,增加了内部通信成本,为了避免一些小概率事件,提供闪断补偿策略,这些都要考虑进去 。如果所有的都去掉 , 那就是比较基础库的性能了 。
所以我只能给出大概数据 , 24核,64G的服务器上,在QoS为message at least,纯粹推,消息体256B~1kB情况下 , 单个实例100w实际用户(200w )协程 , 峰值可以达到2~5w的QPS...内存可以稳定在25G左右 , gc时间在200~800ms左右(还有优化空间) 。
我们正常线上单实例用户控制在80w以内,单机最多两个实例 。事实上,整个系统在推送的需求上,对高峰的输出不是提速,往往是进行限速,以防push系统瞬时的高吞吐量,转化成对接入方业务服务器的ddos攻击所以对于性能上,我感觉大家可以放心使用,至少在我们这个量级上,经受过考验 , go1.5到来后 , 确实有之前投资又增值了的感觉 。
消息系统架构介绍
下面是对消息系统的大概介绍,之前一些同学可能在gopher china上可以看到分享 , 这里简单讲解下架构和各个组件功能,额外补充一些当时遗漏的信息:
架构图如下,所有的service都 written by golang.
几个大概重要组件介绍如下:
dispatcher service根据客户端请求信息,将应网络和区域的长连接服务器的 , 一组IP传送给客户端 。客户端根据返回的IP,建立长连接,连接Room service.
room Service,长连接网关,hold用户连接 , 并将用户注册进register service , 本身也做一些接入安全策略、白名单、IP限制等 。
register service是我们全局session存储组件,存储和索引用户的相关信息,以供获取和查询 。
coordinator service用来转发用户的上行数据 , 包括接入方订阅的用户状态信息的回调,另外做需要协调各个组件的异步操作,比如kick用户操作,需要从register拿出其他用户做异步操作.
saver service是存储访问层,承担了对redis和mysql的操作,另外也提供部分业务逻辑相关的内存缓存 , 比如广播信息的加载可以在saver中进行缓存 。另外一些策略,比如客户端sdk由于被恶意或者意外修改,每次加载了消息 , 不回复ack,那服务端就不会删除消息,消息就会被反复加载,形成死循环,可以通过在saver中做策略和判断 。(客户端总是不可信的) 。
center service提供给接入方的内部api服务器 , 比如单播或者广播接口,状态查询接口等一系列api,包括运维和管理的api 。
举两个常见例子,了解工作机制:比如发一条单播给一个用户,center先请求Register获取这个用户之前注册的连接通道标识、room实例地址,通过room service下发给长连接 Center Service比较重的工作如全网广播,需要把所有的任务分解成一系列的子任务,分发给所有center,然后在所有的子任务里,分别获取在线和离线的所有用户,再批量推到Room Service 。通常整个集群在那一瞬间压力很大 。
deployd/agent service用于部署管理各个进程,收集各组件的状态和信息,zookeeper和keeper用于整个系统的配置文件管理和简单调度
关于推送的服务端架构
常见的推送模型有长轮训拉取,服务端直接推送(360消息系统目前主要是这种),推拉结合(推送只发通知 , 推送后根据通知去拉取消息).
拉取的方式不说了,现在并不常用了,早期很多是nginx lua redis , 长轮训 , 主要问题是开销比较大,时效性也不好,能做的优化策略不多 。
直接推送的系统,目前就是360消息系统这种 , 消息类型是消耗型的,并且对于同一个用户并不允许重复消耗,如果需要多终端重复消耗,需要抽象成不同用户 。
推的好处是实时性好,开销小,直接将消息下发给客户端,不需要客户端走从接入层到存储层主动拉取.
但纯推送模型 , 有个很大问题,由于系统是异步的,他的时序性无法精确保证 。这对于push需求来说是够用的,但如果复用推送系统做im类型通信,可能并不合适 。
对于严格要求时序性,消息可以重复消耗的系统,目前也都是走推拉结合的模型,就是只使用我们的推送系统发通知,并附带id等给客户端做拉取的判断策略,客户端根据推送的key,主动从业务服务器拉取消息 。并且当主从同步延迟的时候,跟进推送的key做延迟拉取策略 。同时也可以通过消息本身的QoS,做纯粹的推送策略,比如一些“正在打字的”低优先级消息,不需要主动拉取了,通过推送直接消耗掉 。
哪些因素决定推送系统的效果?
首先是sdk的完善程度,sdk策略和细节完善度,往往决定了弱网络环境下最终推送质量.
SDK选路策略,最基本的一些策略如下:有些开源服务可能会针对用户hash一个该接入区域的固定ip , 实际上在国内环境下不可行,最好分配器(dispatcher)是返回散列的一组,而且端口也要参开,必要时候,客户端告知是retry多组都连不上 , 返回不同idc的服务器 。因为我们会经常检测到一些case , 同一地区的不同用户 , 可能对同一idc内的不同ip连通性都不一样,也出现过同一ip不同端口连通性不同,所以用户的选路策略一定要灵活,策略要足够完善.另外在选路过程中,客户端要对不同网络情况下的长连接ip做缓存 , 当网络环境切换时候(wifi、2G、3G),重新请求分配器,缓存不同网络环境的长连接ip 。
客户端对于数据心跳和读写超时设置,完善断线检测重连机制
针对不同网络环境,或者客户端本身消息的活跃程度,心跳要自适应的进行调整并与服务端协商,来保证链路的连通性 。并且在弱网络环境下,除了网络切换(wifi切3G)或者读写出错情况,什么时候重新建立链路也是一个问题 。客户端发出的ping包 , 不同网络下,多久没有得到响应,认为网络出现问题,重新建立链路需要有个权衡 。另外对于不同网络环境下,读取不同的消息长度,也要有不同的容忍时间,不能一刀切 。好的心跳和读写超时设置,可以让客户端最快的检测到网络问题,重新建立链路 , 同时在网络抖动情况下也能完成大数据传输 。
结合服务端做策略
另外系统可能结合服务端做一些特殊的策略,比如我们在选路时候,我们会将同一个用户尽量映射到同一个room service实例上 。断线时,客户端尽量对上次连接成功的地址进行重试 。主要是方便服务端做闪断情况下策略 , 会暂存用户闪断时实例上的信息,重新连入的 时候,做单实例内的迁移,减少延时与加载开销.
客户端保活策略
很多创业公司愿意重新搭建一套push系统,确实不难实现,其实在协议完备情况下(最简单就是客户端不回ack不清数据),服务端会保证消息是不丢的 。但问题是为什么在消息有效期内,到达率上不去?往往因为自己app的push service存活能力不高 。选用云平台或者大厂的,往往sdk会做一些保活策略,比如和其他app共生,互相唤醒,这也是云平台的push service更有保障原因 。我相信很多云平台旗下的sdk,多个使用同样sdk的app , 为了实现服务存活,是可以互相唤醒和保证活跃的 。另外现在push sdk本身是单连接,多app复用的,这为sdk实现,增加了新的挑战 。
综上,对我来说 , 选择推送平台 , 优先会考虑客户端sdk的完善程度 。对于服务端,选择条件稍微简单,要求部署接入点(IDC)越要多,配合精细的选路策略,效果越有保证,至于想知道哪些云服务有多少点,这个群里来自各地的小伙伴们,可以合伙测测 。
go语言开发问题与解决方案
下面讲下,go开发过程中遇到挑战和优化策略,给大家看下当年的一张图,在第一版优化方案上线前一天截图~
可以看到 , 内存最高占用69G , GC时间单实例最高时候高达3~6s.这种情况下,试想一次悲剧的请求,经过了几个正在执行gc的组件 , 后果必然是超时... gc照成的接入方重试 , 又加重了系统的负担 。遇到这种情况当时整个系统最差情况每隔2,3天就需要重启一次~
当时出现问题,现在总结起来,大概以下几点
1.散落在协程里的I/O,Buffer和对象不复用 。
当时(12年)由于对go的gc效率理解有限,比较奔放,程序里大量short live的协程,对内通信的很多io操作 , 由于不想阻塞主循环逻辑或者需要及时响应的逻辑,通过单独go协程来实现异步 。这回会gc带来很多负担 。
针对这个问题,应尽量控制协程创建,对于长连接这种应用,本身已经有几百万并发协程情况下,很多情况没必要在各个并发协程内部做异步io,因为程序的并行度是有限,理论上做协程内做阻塞操作是没问题 。
如果有些需要异步执行,比如如果不异步执行,影响对用户心跳或者等待response无法响应,最好通过一个任务池 , 和一组常驻协程,来消耗 , 处理结果,通过channel再传回调用方 。使用任务池还有额外的好处,可以对请求进行打包处理,提高吞吐量,并且可以加入控量策略.
2.网络环境不好引起激增
go协程相比较以往高并发程序,如果做不好流控,会引起协程数量激增 。早期的时候也会发现 , 时不时有部分主机内存会远远大于其他服务器,但发现时候,所有主要profiling参数都正常了 。
后来发现,通信较多系统中,网络抖动阻塞是不可免的(即使是内网),对外不停accept接受新请求,但执行过程中,由于对内通信阻塞,大量协程被 创建,业务协程等待通信结果没有释放,往往瞬时会迎来协程暴涨 。但这些内存在系统稳定后,virt和res都并没能彻底释放 , 下降后 , 维持高位 。
处理这种情况,需要增加一些流控策略,流控策略可以选择在rpc库来做,或者上面说的任务池来做 , 其实我感觉放在任务池里做更合理些,毕竟rpc通信库可以做读写数据的限流,但它并不清楚具体的限流策略,到底是重试还是日志还是缓存到指定队列 。任务池本身就是业务逻辑相关的,它清楚针对不同的接口需要的流控限制策略 。
3.低效和开销大的rpc框架
早期rpc通信框架比较简单,对内通信时候使用的也是短连接 。这本来短连接开销和性能瓶颈超出我们预期,短连接io效率是低一些,但端口资源够,本身吞吐可以满足需要,用是没问题的,很多分层的系统,也有http短连接对内进行请求的
但早期go版本,这样写程序,在一定量级情况,是支撑不住的 。短连接大量临时对象和临时buffer创建,在本已经百万协程的程序中,是无法承受的 。所以后续我们对我们的rpc框架作了两次调整 。
第二版的rpc框架,使用了连接池,通过长连接对内进行通信(复用的资源包括client和server的:编解码Buffer、Request/response),大大改善了性能 。
但这种在一次request和response还是占用连接的,如果网络状况ok情况下,这不是问题,足够满足需要了 , 但试想一个room实例要与后面的数百个的register,coordinator,saver,center , keeper实例进行通信,需要建立大量的常驻连接,每个目标机几十个连接,也有数千个连接被占用 。
非持续抖动时候(持续逗开多少无解),或者有延迟较高的请求时候,如果针对目标ip连接开少了,会有瞬时大量请求阻塞,连接无法得到充分利用 。第三版增加了Pipeline操作 , Pipeline会带来一些额外的开销,利用tcp的全双特性,以尽量少的连接完成对各个服务集群的rpc调用 。
4.Gc时间过长
Go的Gc仍旧在持续改善中,大量对象和buffer创建,仍旧会给gc带来很大负担 , 尤其一个占用了25G左右的程序 。之前go team的大咖邮件也告知我们,未来会让使用协程的成本更低,理论上不需要在应用层做更多的策略来缓解gc.
改善方式,一种是多实例的拆分,如果公司没有端口限制,可以很快部署大量实例,减少gc时长,最直接方法 。不过对于360来说,外网通常只能使用80和433 。因此常规上只能开启两个实例 。当然很多人给我建议能否使用SO_REUSEPORT , 不过我们内核版本确实比较低,并没有实践过 。
另外能否模仿nginx,fork多个进程监控同样端口 , 至少我们目前没有这样做,主要对于我们目前进程管理上,还是独立的运行的 , 对外监听不同端口程序,还有配套的内部通信和管理端口,实例管理和升级上要做调整 。
解决gc的另两个手段,是内存池和对象池,不过最好做仔细评估和测试,内存池、对象池使用,也需要对于代码可读性与整体效率进行权衡 。
这种程序一定情况下会降低并行度,因为用池内资源一定要加互斥锁或者原子操作做CAS,通常原子操作实测要更快一些 。CAS可以理解为可操作的更细行为粒度的锁(可以做更多CAS策略,放弃运行,防止忙等) 。这种方式带来的问题是,程序的可读性会越来越像C语言,每次要malloc,各地方用完后要free , 对于对象池free之前要reset,我曾经在应用层尝试做了一个分层次结构的“无锁队列”
上图左边的数组实际上是一个列表,这个列表按大小将内存分块,然后使用atomic操作进行CAS 。但实际要看测试数据了,池技术可以明显减少临时对象和内存的申请和释放 , gc时间会减少,但加锁带来的并行度的降低,是否能给一段时间内的整体吞吐量带来提升,要做测试和权衡…
在我们消息系统,实际上后续去除了部分这种黑科技,试想在百万个协程里面做自旋操作申请复用的buffer和对象,开销会很大,尤其在协程对线程多对多模型情况下,更依赖于golang本身调度策略 , 除非我对池增加更多的策略处理,减少忙等,感觉是在把runtime做的事情,在应用层非常不优雅的实现 。普遍使用开销理论就大于收益 。
但对于rpc库或者codec库 , 任务池内部 , 这些开定量协程,集中处理数据的区域,可以尝试改造~
对于有些固定对象复用 , 比如固定的心跳包什么的,可以考虑使用全局一些对象,进行复用,针对应用层数据,具体设计对象池,在部分环节去复用,可能比这种无差别的设计一个通用池更能进行效果评估.
消息系统的运维及测试
下面介绍消息系统的架构迭代和一些迭代经验,由于之前在其他地方有过分享,后面的会给出相关链接 , 下面实际做个简单介绍,感兴趣可以去链接里面看
架构迭代~根据业务和集群的拆分,能解决部分灰度部署上线测试 , 减少点对点通信和广播通信不同产品的相互影响,针对特定的功能做独立的优化.
消息系统架构和集群拆分,最基本的是拆分多实例 , 其次是按照业务类型对资源占用情况分类,按用户接入网络和对idc布点要求分类(目前没有条件,所有的产品都部署到全部idc)
系统的测试go语言在并发测试上有独特优势 。
对于压力测试,目前主要针对指定的服务器 , 选定线上空闲的服务器做长连接压测 。然后结合可视化,分析压测过程中的系统状态 。但压测早期用的比较多,但实现的统计报表功能和我理想有一定差距 。我觉得最近出的golang开源产品都符合这种场景,go写网络并发程序给大家带来的便利,让大家把以往为了降低复杂度 , 拆解或者分层协作的组件,又组合在了一起 。
QA
Q1:协议栈大?。笔奔涠ㄖ圃颍?
移动网络下超时时间按产品需求通常2g , 3G情况下是5分钟,wifi情况下5~8分钟 。但对于个别场景,要求响应非常迅速的场景 , 如果连接idle超过1分钟,都会有ping , pong,来校验是否断线检测,尽快做到重新连接 。
Q2:消息是否持久化?
消息持久化 , 通常是先存后发,存储用的redis , 但落地用的mysql 。mysql只做故障恢复使用 。
Q3:消息风暴怎么解决的?
如果是发送情况下,普通产品是不需要限速的,对于较大产品是有发送队列做控速度 , 按人数,按秒进行控速度发放,发送成功再发送下一条 。
Q4:golang的工具链支持怎么样?我自己写过一些小程序千把行之内 , 确实很不错,但不知道代码量上去之后,配套的debug工具和profiling工具如何,我看上边有分享说golang自带的profiling工具还不错 , 那debug呢怎么样呢,官方一直没有出debug工具,gdb支持也不完善,不知你们用的什么?
是这样的,我们正常就是println,我感觉基本上可以定位我所有问题,但也不排除由于并行性通过println无法复现的问题,目前来看只能靠经验了 。只要常见并发尝试,经过分析是可以找到的 。go很快会推出调试工具的~
Q5:协议栈是基于tcp吗?
是否有协议拓展功能?协议栈是tcp,整个系统tcp长连接,没有考虑扩展其功能~如果有好的经验,可以分享~
Q6:问个问题 , 这个系统是接收上行数据的吧 , 系统接收上行数据后是转发给相应系统做处理么,是怎么转发呢,如果需要给客户端返回调用结果又是怎么处理呢?
系统上行数据是根据协议头进行转发,协议头里面标记了产品和转发类型,在coordinator里面跟进产品和转发类型,回调用户,如果用户需要阻塞等待回复才能后续操作,那通过再发送消息,路由回用户 。因为整个系统是全异步的 。
Q7:问个pushsdk的问题 。pushsdk的单连接 , 多app复用方式,这样的情况下以下几个问题是如何解决的:1)系统流量统计会把所有流量都算到启动连接的应用吧?而启动应用的连接是不固定的吧?2)同一个pushsdk在不同的应用中的版本号可能不一样,这样暴露出来的接口可能有版本问题,如果用单连接模式怎么解决?
流量只能算在启动的app上了,但一般这种安装率很高的app承担可能性大 , 常用app本身被检测和杀死可能性较少,另外消息下发量是有严格控制 的 。整体上用户还是省电和省流量的 。我们pushsdk尽量向上兼容,出于这个目的 , push sdk本身做的工作非常有限 , 抽象出来一些常见的功能,纯推的系统,客户端策略目前做的很少 , 也有这个原因 。
Q8:生产系统的profiling是一直打开的么?
不是一直打开,每个集群都有采样,但需要开启哪个可以后台控制 。这个profling是通过接口调用 。
Q9:面前系统中的消息消费者可不可以分组?类似于Kafka 。
客户端可以订阅不同产品的消息,接受不同的分组 。接入的时候进行bind或者unbind操作
Q10:为什么放弃erlang,而选择go,有什么特别原因吗?我们现在用的erlang?
erlang没有问题,原因是我们上线后,其他团队才做出来,经过qa一个部门对比测试,在没有显著性能提升下,选择继续使用go版本的push,作为公司基础服务 。
Q11:流控问题有排查过网卡配置导致的idle问题吗?
流控是业务级别的流控,我们上线前对于内网的极限通信量做了测试,后续将请求在rpc库内,控制在小于内部通信开销的上限以下.在到达上限前作流控 。
Q12:服务的协调调度为什么选择zk有考虑过raft实现吗?golang的raft实现很多?。热鏑onsul和ectd之类的 。
3年前,还没有后两者或者后两者没听过应该 。zk当时公司内部成熟方案 , 不过目前来看,我们不准备用zk作结合系统的定制开发,准备用自己写的keeper代替zk,完成配置文件自动转数据结构,数据结构自动同步指定进程,同时里面可以完成很多自定义的发现和控制策略,客户端包含keeper的sdk就可以实现以上的所有监控数据,profling数据收集,配置文件更新,启动关闭等回调 。完全抽象成语keeper通信sdk,keeper之间考虑用raft 。
Q13:负载策略是否同时在服务侧与CLIENT侧同时做的 (DISPATCHER 会返回一组IP)?另外,ROOM SERVER/REGISTER SERVER连接状态的一致性|可用性如何保证? 服务侧保活有无特别关注的地方? 安全性方面是基于TLS再加上应用层加密?
会在server端做,比如重启操作前,会下发指令类型消息,让客户端进行主动行为 。部分消息使用了加密策略,自定义的rsa des,另外满足我们安全公司的需要 , 也定制开发很多安全加密策略 。一致性是通过冷备解决的,早期考虑双写,但实时状态双写同步代价太高而且容易有脏数据,比如register挂了,调用所有room,通过重新刷入指定register来解决 。
Q14:这个keeper有开源打算吗?
还在写,如果没耦合我们系统太多功能 , 一定会开源的,主要这意味着,我们所有的bind在sdk的库也需要开源~
Q15:比较好奇lisence是哪个如果开源?
关于go语言离线和go语言最新版本的介绍到此就结束了 , 不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。

    推荐阅读