此文是根据周洋在【高可用架构群】中的分享内容整理而成,转发请注明出处.
不知道咱们群名什么时候改为"Python高可用架构群"了,所以不得不说,很荣幸能在此时此刻呢的一个小时里在Python群里讨论golang....
关于push系统对比与性能指标的讨论
很多同行比较关心go语言在实现push系统上的性能问题,单机性能究竟如何,能否和其他语言实现的类似系统做对比么?甚至问如果是创业,第三方云推送平台,推荐哪个?
其实各大厂都有类似的push系统,市场上也有类似功能的云服务.包括我们公司早期也有erlang,nodejs实现的类似系统,也一度被公司要求做类似的对比测试.我感觉在讨论对比数据的时候,很难保证大家环境和需求的统一,我只能说下我这里的体会,数据是有的,但这个数据前面估计会有很多定语~
第一个重要指标:单机的连接数指标
第二个重要指标:消息系统的内存使用量指标
这一点上,使用go语言情况下,由于协程的原因,会有一部分额外开销.但是要做两个推送系统的对比,也有些需要确定问题.比如系统从设计上是否需要全双工(即读写是否需要同时进行)如果半双工,理论上对一个用户的连接只需要使用一个协程即可(这种情况下,对用户的断线检测可能会有延时),如果是全双工,那读/写各一个协程.两种场景内存开销是有区别的.
另外测试数据的大小往往决定我们对连接上设置的读写buffer是多大,是全局复用的,还是每个连接上独享的,还是动态申请的.另外是否全双工也决定buffer怎么开.不同的策略,可能在不同情况的测试中表现不一样.
第三个重要指标:每秒消息下发量
这一点上,也要看我们对消息到达的QoS级别(回复ack策略区别),另外看架构策略,每种策略有其更适用的场景,是纯粹推?还是推拉结合?甚至是否开启了消息日志?日志库的实现机制、以及缓冲开多大?flush策略......这些都影响整个系统的吞吐量.
另外为了HA,增加了内部通信成本,为了避免一些小概率事件,提供闪断补偿策略,这些都要考虑进去.如果所有的都去掉,那就是比较基础库的性能了.
消息系统架构介绍
下面是对消息系统的大概介绍,之前一些同学可能在gopher china上可以看到分享,这里简单讲解下架构和各个组件功能,额外补充一些当时遗漏的信息:
架构图如下,所有的service都 written by golang.
几个大概重要组件介绍如下:
room Service,长连接网关,hold用户连接,并将用户注册进register service,本身也做一些接入安全策略、白名单、IP限制等.
register service是我们全局session存储组件,存储和索引用户的desgo语言相关咨询,以供获取和查询.
coordinator service用来转发用户的上行数据,包括接入方订阅的用户状态信息的回调,另外做需要协调各个组件的异步操作,比如kick用户操作,需要从register拿出其他用户做异步操作.
center service提供给接入方的内部api服务器,比如单播或者广播接口,状态查询接口等一系列api,包括运维和管理的api.
举两个常见例子,了解工作机制:比如发一条单播给一个用户,center先请求Register获取这个用户之前注册的连接通道标识、room实例地址,通过room service下发给长连接 Center Service比较重的工作如全网广播,需要把所有的任务分解成一系列的子任务,分发给所有center,然后在所有的子任务里,分别获取在线和离线的所有用户,再批量推到Room Service.通常整个集群在那一瞬间压力很大.
deployd/agent service用于部署管理各个进程,收集各组件的状态和信息,zookeeper和keeper用于整个系统的配置文件管理和简单调度
关于推送的服务端架构
拉取的方式不说了,现在并不常用了,早期很多是nginx+lua+redis,长轮训,主要问题是开销比较大,时效性也不好,能做的优化策略不多.
但纯推送模型,有个很大问题,由于系统是异步的,他的时序性无法精确保证.这对于push需求来说是够用的,但如果复用推送系统做im类型通信,可能并不合适.
哪些因素决定推送系统的效果?
首先是sdk的完善程度,sdk策略和细节完善度,往往决定了弱网络环境下最终推送质量.
结合服务端做策略
go语言开发问题与解决方案
下面讲下,go开发过程中遇到挑战和优化策略,给大家看下当年的一张图,在第一版优化方案上线前一天截图~
当时出现问题,现在总结起来,大概以下几点
①散落在协程里的I/O,Buffer和对象不复用.
针对这个问题,应尽量控制协程创建,对于长连接这种应用,本身已经有几百万并发协程情况下,很多情况没必要在各个并发协程内部做异步io,因为程序的并行度是有限,理论上做协程内做阻塞操作是没问题.
如果有些需要异步执行,比如如果不异步执行,影响对用户心跳或者等待response无法响应,最好通过一个任务池,和一组常驻协程,来消耗,处理结果,通过channel再传回调用方.使用任务池还有额外的好处,可以对请求进行打包处理,提高吞吐量,并且可以加入控量策略.
go协程相比较以往高并发程序,如果做不好流控,会引起协程数量激增.早期的时候也会发现,时不时有部分主机内存会远远大于其他服务器,但发现时候,所有主要profiling参数都正常了.
后来发现,通信较多系统中,网络抖动阻塞是不可免的(即使是内网),对外不停accept接受新请求,但执行过程中,由于对内通信阻塞,大量协程被 创建,业务协程等待通信结果没有释放,往往瞬时会迎来协程暴涨.但这些内存在系统稳定后,virt和res都并没能彻底释放,下降后,维持高位.
处理这种情况,需要增加一些流控策略,流控策略可以选择在rpc库来做,或者上面说的任务池来做,其实我感觉放在任务池里做更合理些,毕竟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调用.
另外能否模仿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:协议栈大小,超时时间定制原则?
消息持久化,通常是先存后发,存储用的redis,但落地用的mysql.mysql只做故障恢复使用.
如果是发送情况下,普通产品是不需要限速的,对于较大产品是有发送队列做控速度,按人数,按秒进行控速度发放,发送成功再发送下一条.
是这样的,我们正常就是println,我感觉基本上可以定位我所有问题,但也不排除由于并行性通过println无法复现的问题,目前来看只能靠经验了.只要常见并发尝试,经过分析是可以找到的.go很快会推出调试工具的~
是否有协议拓展功能?协议栈是tcp,整个系统tcp长连接,没有考虑扩展其功能~如果有好的经验,可以分享~
系统上行数据是根据协议头进行转发,协议头里面标记了产品和转发类型,在coordinator里面跟进产品和转发类型,回调用户,如果用户需要阻塞等待回复才能后续操作,那通过再发送消息,路由回用户.因为整个系统是全异步的.
不是一直打开,每个集群都有采样,但需要开启哪个可以后台控制.这个profling是通过接口调用.
Q10:为什么放弃erlang,而选择go,有什么特别原因吗?我们现在用的erlang?
erlang没有问题,原因是我们上线后,其他团队才做出来,经过qa一个部门对比测试,在没有显著性能提升下,选择继续使用go版本的push,作为公司基础服务.
Q11:流控问题有排查过网卡配置导致的idle问题吗?
流控是业务级别的流控,我们上线前对于内网的极限通信量做了测试,后续将请求在rpc库内,控制在小于内部通信开销的上限以下.在到达上限前作流控.
还在写,如果没耦合我们系统太多功能,一定会开源的,主要这意味着,我们所有的bind在sdk的库也需要开源~
项目描述:Pprof made easy at development time for Go
项目描述:ObjectBox Go - persisting your Go structs/objects superfast and simple
项目描述:Scan and analyze OSS dependencies and licenses from compiled Go binaries
项目描述:Fast thread-safe inmemory cache for big number of entries in Go
项目描述:Go library for building event-driven applications.
项目描述:high performance fixed decimal place math library for Go
项目描述:a tiny software that receive a smtp request (email) and send it to the specified webhook as a http post request
项目描述:Compile-time Dependency Injection for Go
项目描述:Go implementation of Linux Network Block Devices
项目描述:Go package for caching DNS lookup results in memory.
主页地址:
项目描述:Go Firecracker SDK
项目描述:A standalone lightweight full-text search engine built on top of blevesearch and Go with multiple storage (scorch, boltdb, leveldb, badger)
项目描述:Fast web fuzzer written in Go
项目描述:dogsled is a Go static analysis tool to find assignments/declarations with too many blank identifiers.
项目描述:Plugin-driven CLI utility for code generation using Go source as IDL
项目描述::books: <<青云QingCloud云计算入门 - 基于Go语言>>青云直上-只上青云-云之基石-自由计算(还未启动...)
项目描述:Fast Golang PCAP Reader Benchmark Comparison
golang AES加密函数的使用
AES: Advanced Encryption Standard
高阶加密标准,是用来代替 老的DES的.
ECB:Electronic Codebook Book
CBC:Cipher Block Chaining:这是最常见的块加密实现
CTR:Counter
CFB:Cipher FeedBack
OFB:Output FeedBack
具体的差异我也没去弄明白,知道这么个意思,加密算法稍后差异.
包括AES,CBC,CTR,OFB,CFB,GCM.
GCM实现算法不需要pad.
以上就是土嘎嘎小编为大家整理的desgo语言相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!