张新宇-《 Go分布式实时服务架构 》

张新宇 / 目前担任TM+系统架构师,主要负责TM+服务架构设计及开发。前端

15+年的软件开发经验,8年互联网架构经验,曾任职于盛大游戏、沪江网、途牛旅游等企业,参与架构设计、开发了多个公司级核心项目。java


前言python


很荣幸展现一下咱们的架构,包括分享一下我在GO方面的一些实践,但愿能给你们带来收获。我今天将经过这四个部分来给你们介绍Go分布式实时服务架构:mysql

1、实时系统介绍react

2、服务架构设计golang

3、经验总结及收获web

4、 将来展望算法




实时系统介绍
sql


TutorMeet+介绍数据库


首先是实时系统的介绍,TutorABC是在线音视频实时的系统,就是直接经过网络能够跟外籍的老师进行面对面的沟通的系统,能够和说当地正宗语言的外籍人士进行交流,在这过程当中咱们的英语包括其余技能能获得有效提升,跟着专业的外籍人士学英语,能解决不少咱们跟着中国人学英语而遇到的因文化背景差别致使英语教学上出现的尴尬。下面是咱们TutorMeet的一些数据:服务全球135个国家,包括100多个城市的外籍顾问和学习者;最关键的咱们是7×24小时,365天整年无休提供服务;每一年提供超过千万级别,如今多是更大了,如今咱们天天并发数有几万的在线课堂,这个课堂并非一对一的,最大的达到1对6,1对8都有,咱们的云讲堂是1对几万,一个讲堂里面几万个学生,一个老师这样的互动。


实时系统特色


高性能


实时系统的特色之高性能,指令要求高速处理,假如指令有必定延时,那他会累计下来,这时咱们会看到这样的状况。教室里的老师在白板上写字,学生看到时就已经延时了,老师写的和他讲的彻底匹配不上;或者是老师在作答题的时候,学生回答时收的指令晚了,老师把答题回收就致使学生的体验很是差,因此高性能是咱们一直追求的目标。


咱们的音视频支持大码率的传输,特别是视频上面,若是是有一些现场发布或者是大的场合下面会有一些大码率数据的传输,咱们经过跨防火墙的限制,包括网络的稳定性波动会带来很大的丢包,大码率的传输上面也是咱们追求的性能。并发教室规模,咱们的教室除了咱们规划好的教室之外,也就是今天排好的之外,可能还有不少的体验课,就好比说咱们的销售团队在对外作了一个活动之后,这个时候有一个巅峰流量进来,这个时候系统必定要撑得住,撑不住会致使咱们的销售的成本就白作了。


低延迟


前面已经讲到了低延迟,咱们的音视频已经作到了低延迟,因此指令也要与之同步,假如指令响应太高或者是反馈太高就会出现前面讲的现象,多人音视频的实时同步,好比说1对6,1对8,你们同时在一个房间一块儿在说话,这时实时同步的音频,视频结构要知足咱们的需求,否则就会出现你一言我一语,彻底不知道讲什么,延迟带来的问题就会很严重。


稳定性


目前,咱们是多个异地机房灾备,服务稳定性基本达到了4倍多,因为快速的故障切换,好比某个机房断了或者是哪一个光缆断了,咱们会在几分钟以内作快速两个机房的切换,这个是知足了稳定性的需求。


监控性


咱们的音视频是采起直接在网页上,让用户无安装无成本的直接打开网页就接连上的,因此设备会给咱们形成不少的困扰,例如用户的摄像头、麦克风、操做系统以及如今有一些用户还在用着XP,这时候就须要咱们的客服人员或者是技术人员实时的可以隐身进入这堂课里面,实时对用户线下的捕获,作用户数据的采集,可以针对用户设备提供智能的检测这样系统的需求。


TM+业务发展历程


TM(旧时代)解决方案(< 2016)


Tutormeet的发展不是一次性成功的,它已经运行了将近10年时间,都是硅谷团队基于Flash的技术站,目前Flash已经被各大厂商抛弃了,之后的设备会愈来愈少用flash,可是实现的是挺好的,技术自己的特色也带来了不少的局限性,2016年之前Tutormeet基本上是flash的方案。


640?wx_fmt=png

      

TM+1.0解决方案(2016-2017)  


Tutormeet的技术原型阶段,在2016-2017年基本上采用了Webrtc作的,由于那时咱们对整个音视频的支持包括中国复杂网络的适配是有一个原型测试的阶段,这个时段也是跟flash并行跑的。原型阶段由各个团队提供的技术是很是复杂的,有nodjs前端的,他们作前端的打包,包括浇水层等,还有python是语言数据的传输,还有Vuejs,有C++,还有有部分GO的。主要的问题是技术栈过多,致使了成本太高,人员维护的成本也很高,由于每一个技术栈都有本身的库包括代码结构,而后链接复杂,这个版本是前端经过web链接指令,链接媒体,由于webrtc要传输一些FDD打隧道的指令,还有前端的数据打点、监控等不少的链接,一个前端可能链接了N多的。


当一个系统的输入量多了之后带来的问题会很是多就致使整个系统混乱的根源,它的音视频的引擎,咱们基于开源的是Kurento,这个已经被一个公司收购了,咱们经过自身研究的代码有一些性能问题,好比基于Kms,是会带来很重的内存不释放的问题,这个是咱们2016年到2017年的原型阶段,对咱们的最大的好处是把整个的流程所有跑通了,包括整个的运维,包括整个的思路已经肯定好了,最后,因为在原型阶段咱们经历过屡次失败,最终肯定了以GO为技术上核心的主要思路,GO+C++这样的主要思路,提供了2.0的方案,目前今年的2.0的方案。

     

640?wx_fmt=png

      

TM+2.0解决方案(2017 - 2018)


这个阶段是产品阶段,目前已经并发团队。以GO为核心,前端的reactjs,C++的核心技术栈。彻底是本身控制,而且合并链接,优化了传输,好比说咱们国外的媒体网关会把国外的音视频流经过SFU数据流控过来到咱们的主机房,而后视频流的处理,咱们提供了自建的MCU,提供了一些全功能媒体处理能力,包括音视频混流还有转码,咱们还提供了大会堂的承载能力,而后提供了全链路监控,能够实时进入教室处理。这个是目前为止2.0的优化的成果,基本上已经彻底替代了之前的flash和1.0的版本,最基本的是GO。GO带来高性能的同时,而且带来高的开发效率。


640?wx_fmt=png

      

TutorMeet+界面的介绍


这个是咱们的整个上课界面,你们能够看一下,实际上结构仍是蛮复杂的,主要是除了音视频的处理,有消息聊天,还有实时的白板互动,稍有一些延迟或者是丢包就会很明显,学生的界面还有后台监控的数据,提供了在线实时的数据,而且提供报警的功能,TutorMeet+还有自建的监控的全链路的平台。


640?wx_fmt=png

       

TM+相关的开源项目


目前咱们用到的相关的技术有这些,主要是以容器为核心,几乎已经达到99%的服务都在容器中跑了,而后注册与发现都是经过consul进行的,整个服务的开发源是GO,音视频的C++,咱们在实时跑的数据里面拿Redis当数据库用,mysql是源数据保存。咱们的旁路有日志服务,包括kafka,kibana等等,包括监控有grafanalabs等等,还有一些网关,好比说一些caddy作了二次的开发。


640?wx_fmt=png

      

目前有95%主要的开发语言是GOlang和C++,还有一些其余的相关的系统是其余语言的实现,咱们总体的音视频是抗丢包率是25%在不好的状况下25%感受不到音视频有延迟,全端覆盖,PC端安卓、IOS,包括浏览器。100毫秒的端对端延迟,包括支持全高清模式的传输。


服务架构设计


TM+ 2.0系统布局


接下来是介绍服务架构的设计,你们能够看到咱们整个的界面上有不少的功能,每个功能对应的后台都是一些服务,前端方面,当你访问咱们的Url的地址他会根据你的历史信息包括目前提供的可用的状况会分配你,咱们后台分布两层,一个是指令层,另一层是媒体层,有一层是走UTP的由于追求高性能,这边是走的TCP的包,gateway是保持链接,不管是手机4G网络等等,而后把链接从国外引入到咱们的roomservice,而后再进行分发传递给gateway返回到前端,固然今天我重点会介绍咱们前面的指令层。


640?wx_fmt=png


包括MCU,包括分析服务,推流包括录制服务,包括媒体控制等等这些在音视频当中会提供。我主要介绍的是咱们指令层,包括了白板、一些技术服务、状态等,而后咱们的跨平台的支持有服务发现,咱们提供的服务发现包括了数据存储,包括第三方的,第三方指的是还有会员和还有排课等不少其余的系统接口,包括对微信的一些接口都是在技术上提供的。而后咱们还提供了监控服务,好比说各个服务的健康情况,一个指令的延迟情况,包括咱们的学生端接收到SRUTPRPT的状态参数,包括丢包,延迟,包括采集音视频数据的实时的状态会反馈,这个就是咱们大体的系统的功能划分。


Zone说明  


具体到某一块是这样的,咱们的用户经过先访问路由,会分配他可用的网关,好比说国外老师就会访问国外的网关,例如你来自于欧洲咱们反馈你德国或者是法国的网关,而后你来自中国好比说是广州,咱们会广州机房的网关分配给你,保持链接,而且把客户端的指令实时发送到主机房的roomserver上面来,这里有一个问题,当你课尚未开的时候,老师和学生同时登录进来的时候会有一个冲突,由于没有开的时候我不知道你的roomserver在哪?开了之后第一我的进来我能够分配,若是是两我的同时进来有可能会分错,因此说这个地方采用了一个一致性的算法,就是当两我的同时进来,咱们的服务缓存里面并无,咱们会两边投票出一个roomserver把指令丢给他,全部的服务咱们的都是无状态的,这台机器挂掉也不影响,你到其余的机器上面能够接着上,由于状态都是保证在集群里面,只要发现咱们的数据这个房间里面没有数据,咱们都会到集群里面把集群恢复回来,由roomserver再进行就近的服务把指令丢给他们,由于是实时系统,因此说主流层都是基于PCT的长链接。


咱们在心跳检测断的时候都会两边再进行投票,投票出来公共决策的,因此说这就保证了高可用。roomserver当前端有webrtc的请求协议或者是打洞的信息传递过来,咱们的roomserver会经过媒体控制单元去分配它的gateway和防火墙的信息,生成sttp的返回给前端,而后就经过gateway广播的数据传递,这个模型在原来是很困难的,并无提供SFU的功能。而后咱们全部的这种服务器的健康数据都会采集到一个收集器里面,一样会影响到咱们的,若是哪一块服务挂掉了咱们会有一个闭环控制到前端进行再分配。


640?wx_fmt=png


这部分就是我说的分配算法,包括了一致性hash,你们能够看到前面这么大一块,只是一个zone就是可工做区,就像一个工做盒子同样,整个的服务都合成在一块了,每个服务都是作容器化的管理,那这个zone有什么用处呢?也是咱们经过好久摸索出来的一个经验,这个就是标准的打包方式,咱们经过服务推到线上。


Zone优点


Zone的优点有这几点:一、进行服务管理,能够实时的调度上架下架;二、能够进行负载分流,若是销售团队作了一次推广活动一下引来了几十万的流量,你们都来开体验课,咱们会实时开工做区进行负载分流;三、故障转移,假如咱们哪个Zone出现故障了,好比说是有程序或者是由线路致使的故障,咱们会动态的调到其余的可用区里面;四、动态扩展,例如老师数量增长了,咱们会进行动态扩展增长资源的部署;五、蓝绿发布,咱们的各个版本发布上去之后能够在线作一个实时的调整,若是发现2.0版本出现的问题的话,我能够实时回转到1.0版本,首先是把流导走,导到其余的稳定的区去。


TM+ 2.0系统布局


     

640?wx_fmt=png

      

咱们整个的架构是这样的,咱们分红各个工做中心,这个工做中心能够理解成可用机房,可是一个异地的机房,机房里面分出N多的可用区,红线的地方是新版本发布了,可是如今是0%的状态,全部的压力都承载在这些黑的可用区里面,而后能够分1%的流量而后咱们再跑,若是发现这个上面跑了之后没问题,咱们再分5%,10%,50%,100%,若是说是发现有问题之后,咱们实时把这个流量经过前面的控制指令导走,导到其余的稳定区里面,这样整个容器就下载下来了供咱们使用。

    

640?wx_fmt=png

  

整个的工做中心,就是能够理解成是各地的机房,咱们机房之间也是作了异地的,若是整个的机房挂掉了,能够迁到阿里机房或者是腾讯机房这样不一样的方式,除了这种切分的方式之外,还有一些统一的共用的数据,由于数据库要存储一些开课信息,用户信息,学生信息,咱们的路由包括控制器,就是咱们的服务发现的一些控制,一些是日志和性能收集的MQ,还有一些监控的东西会放统一的地方,可是咱们会用异地的载备,这样总体上保证了咱们全部的服务可用区里面,不管挂掉哪一个,包括数据中心都会有一个高可用的方案存在,就不会存在在单点和并发压力。有时会想,咱们的架构在设计的时候有存在着不少的权衡,实际上均可以,总之是实时作各类各样的权衡。


RPC? HTTP vs TCP


咱们在作一些服务时,会考虑究竟是用HTTP仍是TCP?基于 HTTP 的 RPC 咱们作了一些尝试,实际上达不到实时的需求,为何呢?首先是是第七层的协议,有额外的头部信息包括了一些传输的控制,还有不少你们了解到 http 是基于 ask 码的信息,固然这样会带来更多的工做和问题。固然,最好是有很成熟的负载均衡的方案,它的 rest 比较清楚,服务提供的比较清楚,它的Keep alive的模式也能够进行数据长流的推送,甚至你能够经过代码实现了双向的同步,可是有一个最大的问题是一个请求和响应是同步的,你一个请求过去响应回来才能发下一个请求再响应。

    

640?wx_fmt=png

      

RPC via http2


这个就是咱们当初作Http作RPC的限制,致使了咱们只是作一些旁路的服务,所谓旁路的服务就不会是主流的服务,咱们最终选择的是经过tcp原始的处理,而后咱们的封包的指令是选择的protobuf,轻易突破了10W的QPS的指标。

     

640?wx_fmt=png

      

咱们当初也参考了gRPC,也打算在一些后续的领域里面继续在这方面进行gRPC的尝试,据咱们了解下来成熟度很高了,也避免了一些http1.0带来的问题,包括前面说的你的请求和响应必须是同步的方式,而且也支持了双向的通信,可是怎么说?对于一个音视频指令来讲仍是太重,咱们仍是选择了在其余的旁路的服务里面使用了gRPC,包括QUIC协议,由于咱们有大量的从国外传递的数据,包括这样的一些指令的传递,QUIC基于UTP的协议来讲更于整个的链接和响应速度有效,可是很遗憾国内大部分的厂商和设备对这个支持仍是很差,咱们期待下一步的发展。


序列化选择 protobuf vs json vs gob


序列化选择,方案里面这三种咱们都有,最主要的是PCB+PB,前面过来的是一些json的数据,网关会作一些协议的转化,咱们采用了golang内置的gob,可是有一个好处不须要额外的描述,而且支持的数据结构都是最丰富的,也是侵入性最小的一个,咱们用它来作一些数据的register化,上课的回放,还有kafka的存储等等,由于咱们都是golang的,因此gob也作了使用,基本上性能是从高到低的分布。还有一个不少的时候咱们是有动态类型的,好比说类型里面有不少的案例,咱们不关心会直接透传或者是广播会定义不少的bytes和any,对于json对应的就是rawmessage。

     

640?wx_fmt=png

      

容错机制 failfast vs failover vs F&F


在容错机制方面,咱们也是纠结了好久,当遇到指令发送到下一级处理服务的时候,若是失败了,正常的按照咱们的微服务的概念来讲,应该是重试,而且对次数进行控制,实际上重试完了以后多是在学生端前端用户的地方人家已经翻页了,那这个指令就是无效的,而且会带来错乱,因此咱们提供了快速失败,若是说这条指令没有传递过去,咱们会有一个备选的线路直接发备选线路和你这条指令会作重试,接收端会作趋同和广播,若是备选的也挂了的时候,这个就报警了,这时这个指令就会主动的丢包。


对于旁路的指令会进行正常的从新重试,从新分配下一个指令的数字常识之后,从新分配下一个服务链路。参数收集是最不用担忧的,RTCP的参数,包括解码的参数收集咱们使用的是fire覆盖,分级报警,若是有这些东西失败了之后,一次性报警会把你的邮箱甚至是微信都弄崩溃,因此咱们在报警的时候作了不少的趋同,包括了关联的判断,好比说这个报警是因为某一个已报过的故障引发,因此咱们作了这样不少的优化的工做。而后分组处理,就是说咱们针对已知的问题的状况下会有一些自动的处理,一些补偿,若是是不在操做库里面补偿的时候,会转为人工干预整个的服务。


640?wx_fmt=png

        

经验总结


Goroutine vs map


咱们使用GO的时间不是很长,从2016年开始到如今,由于之前咱们是从 C++ 和 JAVA 转过来的,因此有一些问题和你们共同的探讨。首先值得分享的是golang的map,在1.9之前是不提供现场安全的map,咱们在线上使用的时候大量碰到了同步的问题,由于咱们一个房间一个用户就可能有成千上百个状态修改,若是map不强壮会致使不少的问题。


初版本咱们简单作了一下,就用了sync.Mutex锁住,后面用了读写锁,带来的性能的损耗仍是挺大的,基于这种状况下咱们本身提供了 concurrentHashmap,第一个就是最小化的锁,优化了rehash算法,通过咱们测下来的速度远比它高不少,这个只是第一步,在后面的几步,map上线了之后就发现了内存比较平缓的直接上去,为何呢?


经过检查之后发现了不少的历史数据里面会存于 hashmap 里面,下课的时候会作轻地因为运维的工具和其余的工具会从新的访问这些服务,会读取当时的状态致使这些数据又从reads回来了,咱们基于hashmap作了二次的优化,咱们又提供了LRU的算法包装,截断不少后体现不是很明显了,那就继续优化,LRU和LFU的算法提供下咱们又对它进行了再装饰,提供了一个主动过时的时间,不容许数据在本地缓存里面停留太长时间,若是停留太长时间,我认为会对整个的程序带来一些影响,因此从一个map,一个runtime发展到map,也是经历了不少的工做。


640?wx_fmt=png

      

DNS


第二个问题是在大量的线上的并发状况下会出现这样的问题,在作旁路的请求的时候,偶尔会发生DNS很少了的状况,和运维找了好久,后来发现 golang 会提供两种方式,一个是 Cgo,另外一个是纯GO,这个是基于文件在下面有一个Edc的文件,若是出现了不支持的关键字就会起用CGO,由于咱们的DNS运维会根据实时的动态配置,因此很难对他进行要求,说不能加这个外面的关键字这个很难,而后咱们就强制让他在运行的时候加入这个变量,让他强制使用GO,纯GO的实现方式。


640?wx_fmt=png

      

咱们在运行咱们的服务的时候,会发现某一些服务器上面的链接会迅速递增,链接数会迅速的递增,而后爆出文件耗死,耗干的状况。


http issue

            

640?wx_fmt=png

   

我举出这样的例子,咱们有一些同窗在写请求的时候写一个 client,由于咱们要起用 keepalive,咱们在dialcontext进行超时的设置,而后也受到roomserver的注意了,就是关闭client这样的代码,实际上咱们测下来并无真正的关闭,由于底层里面有一个 dofo ,若是设置的时候,把client做为惟一性的或者是把transport弄下来,若是这个池每次建立都会建立一个链接池链接,而且关不了,而后会致使愈来愈多,最后把资源耗死。


640?wx_fmt=png

  

咱们主要是执行了这样的一些命令作一个文件发现,第一个解决的方式是还有一种若是你不须要去链接共享,你能够关掉,每次我都连一次完了,还有一种是去作transport的池化存储,每次建立就能够把这个给他,这样就能够共享池。固然了系统层面上也有一些设置,包括ulimit,包括lacal,port,range,包括tcp的关闭时间,尽可能的缩短能够减小不少时间损耗。


640?wx_fmt=png

   

loop issue  


接下来是咱们常常常见的处理方式,咱们有一个stream,各个端把数据汇到这里,而后每次读出来一个msg进行处理,整个for的循环进行处理,逻辑就是全部的链接把数据丢进来进行逻辑处理。若是是中小型并发量处理并无问题,每秒钟几十万的并发量,这是一个关键,假如在平时,几毫秒内处理完这个没有问题,在有异常的状况,假如网络环境也包括内存的致使了handle函数慢了10几毫秒,就会致使stream大量的堆积,延迟会愈来愈高,不少的时候咱们会发现容器刚刚启动的时候全部的性能没有问题,跑了愈来愈久就发现这个数据经过咱们打在包里的时间会看到累计的时间愈来愈多,这个是累计的问题。


640?wx_fmt=png

      

解决方式也很简单,第一个是把它容量扩大,另外一个是弄一个批处理,不断的append进去作批次的处理,这个方式是指某一些状况,并非适合全部的状况,不少的时候有一些依赖关系就很难作。

     

640?wx_fmt=png

         

而后还有一种方式,作的提供一个协商的池化,能够并发16个池,全部的sema建立一个chan,全部的指令读出来之后进入这个池,当它满了16个之后就会锁住,这个go继续执行,当处理完之后就读出来放一个进来,读出来一个放一个进来,它不容许并发太多的无限制的使用。

     

640?wx_fmt=png

      

performance issue   


而后还有说的一点是咱们的一些 performance,全部的依赖必须加入 context.WithDeadline (WithTimeout) ,就在本机房,网卡没有问题。没法肯定全部的问题,全部的操做都是保证加大延迟的时间,若是不这样的话,咱们上次就不会有太多的损耗,可是架不住有一些工具,固然了这个是其余人写的工具,里面用了一些keys等等,而后就崩溃了,监控中心找到,而后堆到下面就产生了雪崩的效应,这个你们应该也清楚,全部的这个failover的操做仍是基于合理的控制范围内。

     

640?wx_fmt=png

       

将来规划


GO 发展规划


对于将来的规划,首先咱们的升级并非那么的激进,咱们目前仍是在1.9的版本,可是咱们对最新的1.11版本表示有很大的热情,包括了引入Go Module 的打包都会用于1.1的服务。目前让咱们痛苦的是,管理的 GO module 的工具都缺少版本控制,咱们技术进来搭 GO 环境的时候最喜欢拉最新的版本跑,跑完发现不少方面存在问题,须要作统一的包管理,包括版本的控制会对整个的统一化的提升,我我的以为这个统一的包管理也是golang的一大痛点,但愿后续可以把这个包管理得像java可以作大作全。而后咱们会去研究 Wasm with go,对它有很浓的兴趣是由于不少时候咱们的解码能力,包括三维处理都能靠服务系统,若是是能在浏览器端提供基础不用太多基础的音视频解码能力,或者是三维处理能力,会对整个的服务的架构体系会带来很高的但愿。

     

640?wx_fmt=png

      

基于 GO+Docker 组件化平台,支持热部署和热更新,目前咱们是整个的按照微服务的框架,可是咱们但愿之后会把微服务拆的更细,相似于一个组件的格式,提供按需的拉取,按需的发布和更新架构的方式。咱们目前正在研究的QUIC也是咱们很感兴趣的,包括 service mesh,还有一些lstio的研究,包括咱们的 feedback 实时的响应,把用户的参数,性能参数实时的反馈,而且有效的经过代码的形式进行影响的机制。


TM+容器化发展规划


最后是容器化的发展,咱们目前是基于一个公有云和私有云的混合模式,但愿之后可以发展成基于微服务化混合型和按需分配的模式。

     

640?wx_fmt=png

      

下面是咱们音视频方案更好的网络建设,更快更灵活,就是打通中国到国外的线路瓶颈,支持4K,8K高清的音视频,包括今年很热的h265,AV1,以及AI和VR,AR的结合,咱们在旁路作数据引流,包括AI分析的同时会提供AI的切入和转化,这一块也是在咱们的研究当中,还有一些微服务化的scaleout的更新的响应。

     

640?wx_fmt=png

    

个人演讲就到这里,谢谢你们!


end