“大系统小做”,微服务与腾讯的理念有一些相同的地方。本文整理自许家滔在2016年ArchSummit全球架构师峰会的演讲,分享了微信在微服务架构的实践中遇到的问题与解决方案。
背 景

首先,我们需要敏捷开发。过去几年,微信都是很 敏捷 地在开发一些业务。所以我们的底层架构需要支撑业务的快速发展,会有一些特殊的需求。

另外,目前整个微信团队已经有一千多人了,开发人员也有好几百。整个微信底层框架是统一的,微信后台有千级模块的系统。比如说某某服务,有上千个微服务在跑,而集群机器数有几万台,那么在这样的 规模 下,我们会有怎么样的挑战呢?

我们一直在说“大系统小做”,联想一下,微服务与腾讯的理念有哪些相同与不同的地方呢?通过对比,最终发现还是有许多相通的地方。所以我挑出来讲讲我们的实践。

看过过去几个会议的内容,可能大家会偏向于讲整一个大的框架,比如整个云的架构。但是我这边主要讲的是几个特殊的点。

概 览

开始看一下我们的结构。全球都有分布,主要有上海、深圳、香港、加拿大几个数据中心。

其中上海服务国内北方的用户,深圳负责南方用户,加拿大服务北美、南美和欧洲,香港服务东南亚、中东和非洲地区。

然后来看看我们的架构:

  • 最上边是我们的产品;

  • 然后有一个号称几亿在线的长连接和短连接的服务;

  • 中间有一个逻辑层,后台框架讲的主要是逻辑层往后这块,包括我们的 RPC、服务分组、过载保护与数据存储;

  • 底层有个 PaxosStore 存储平台。

整套就是这么个体系。微服务很容易去构建,但是规模变大后有哪些问题,需要哪些能力?这里我就挑出三个点来讲一下,下边会具体来讲讲解决方案:

敏捷

希望你的服务很快实现,不太多去考虑。像我们早期互联网业务,甚至包括 QQ 等,我们很注重架构师的一个能力,他需要把握很多的东西。他设置每个服务的时候,要先算好很多资源,算好容灾怎么做。

容灾这个问题直接影响业务怎么去实现的,所以有可能你要做一个具体逻辑的时候要考虑很多问题,比如接入服务、数据同步、容灾等等每个点都要考虑清楚,所以节奏会慢。

容错

当你的机器到了数万台,那每天都有大量机器会有故障。再细一点,可以说是每一个盘的故障更频繁一点。

高并发
基础架构

接下来看看我们的基础架构。

整个微服务的架构上,我们通常分成这些部分:

  • 服务布局

  • 服务之间怎么做一些远程调用

  • 容错(主要讲一下过载保护)

  • 部署管理

服务布局

分两层,一个是 城市间。城市之间的数据是相对独立的,除了少数账号全球同步,大部分业务都希望做成电子邮件式的服务,各自有自身的环境在跑,之间使用类似于电子邮件的通信。

所以我们选择让每个城市自治,它们之间有一个 200-400ms 的慢速网络,国内会快点,30ms。

城市内部,就是每个园区是一套独立的系统,可以互相为对方提供备份。要求独立的电源与网络接入。

城市内部会有整套的划分,终端 -->接入层 -->逻辑层 -->存储层 都是完全独立的一套系统。

远程调用

看到很多框架,竟然是 没有协程 的,这很诧异。早年我们 QQ 邮箱、微信、图像压缩、反垃圾都是一个 web 服务,只有存储层会独立到后面去,甚至用 web 直连 MySQL。因为它早期比较小,后来变大之后就用微服务架构。

每个东西都变成一个小的服务,他们是跨机的。你可以想象一下,每天我们很多人买早餐的时候,掏出手机做一个微信支付,这一个动作在后台会引起上百次的调用,这后边有一个复杂的链路。

在 2014 年之前,我们微信就是没有做异步的,都是同步的,在这么多调用里,A 服务调用 B,那要先等它返回,这样就占住了一条进程或者线程。所以其实 13 年的时候,我们发生了大大小小的故障,很大一部分原因就在这里。

然后 13 年底的时候,这个问题太严重了,严重到,比如发消息的时候,你去拿一个头像之类的,它只要抖动,就可能引发整一条调用链的问题,并且因为过程保护的不完善,它会把整个消息发送的曲线掉下去,这是我们很痛苦的时间。

然后当时我们就去考虑这些方案,13 年的时候抽出 3 个人重新做了一个完整的库 libco。(两千行),实现 时间轮盘事件处理链常用网络编程模式同步原语 等。它分为三大块,事件驱动网络 HOOK协程机制

早期是多进程为主,当年切多线程的时候,也遇到一大波修改,后来线程里有了一个线程变量就好多了。如果没有这个东西,你可能要把许多变量改成参数再一层一层传递下去。有了线程变量就好多了。现在我们的协程变量也是这个意义,效果就像写一个宏一样。

另一个是,我们支持 CGI,早期库在 CGI 上遇到问题,所以没有推广。因为一个标准 CGI 服务是基于一些古老的接口的,像 getENV、setENV,就是说你的 coreString 是通过 ENV 来得到的,那么这个我们也把它给 HOOK 掉了,它会根据你的协程去分派。

最难的一个是 gethostbyname 方法,我发现很多人就连在异步编程里,处理 hostbyname 也可能是用了一套独立线程去做,或者你很辛苦地把整个代码抠出来重新写一遍,这个肯定是有很多问题的。所以我们 libco 就把这个 gethostbyname 给完整地支持了。

最后如果你还不爽,说一般业务逻辑可以这么干,那我还有很多后台代码怎么办呢?很多有经验的老的程序员可能要拿着他们那一堆很复杂的异步编程的代码来质疑我们,他们不认为他们的代码已经完全可以被协程所取代了。

他们有如下两个质疑:

  • 质疑性能:协程有很多切换,会不会带来更大开销?

  • 质疑承载能力:你可能处理几万并发就好,消耗个 1G 内存就行,但是我们这里是处理千万并发哦,这么大的规模,我不信任你这个东西。

这样我们其实是面临了一个问题,因为一些老代码,越是高级的人写的,它的技术栈越深,稍微改动一点代码,就出 BUG 了。

所以我们后来做了两个东西,一个是实际修改了相对简单的异步代码到 libco 里,然后性能更好了。

因为在做异步编程的时候,你需要自己去维护很多的数据结构,做你的状态保存,它们的生存期有可能需要很久,你自然地会分配许多内存给它,当然你会用一些内存池去优化它,但是这些是有限的。

但是你用协程的话,很多变量就自然在一个连续的内存里了,相当于一个小的内存池,就比如 if…else… 这个你没有必要去 new 一个东西保存状态的,直接放在栈里就行了,所以它的性能更好了。

第二个是,它要求很高的并发。由于协程要一个栈,我们一般开 128k,如果你对这个代码掌控得比较好,可能开 16k,就算是这样,你要开 1 万个协程,还是要 100 多 M 的内存。所以我们后来就在这基础上做了一个可以支持千万连接的协程模式。

libco 是一个底层库,让你很方便开发,但是大部分开发人员不是直接面对 libco 的,我们花了一年时间把整个微信后台决大部分逻辑服务、存储服务改成基于 libco,整个配置就直接通过配一台机器上的并发数配 10 倍甚至 20、30 倍,这样子就一下子把整个问题解决了。

过载保护

并发数上去后容易引发另一个问题,早期的时候,后端服务性能高,逻辑服务性能相对弱,很容易被 hold,不可能给后端发起很多连接,不具有“攻击性”,但修改完成后,整个前端变得很强,那可能对后端产生很大的影响。这个时候就要来考虑一下 过载保护 了。

一般会提到几个点。

轻重分离

就是一个服务里边不要又有重的操作,又有轻的,这样过载的时候,大量的请求都被某些请求拦截掉了,资源被占满了。

队列

过载保护一般是说系统内部服务在做过去的事情,做无用功。它们可能待在某个队列里边,比如服务时间要求 100ms,但它们总是在做 1s 以前的任务,所以整个系统会崩溃。

所以老的架构师会注重说配好每一个服务的队列长度,估算好。但是在繁忙的开发中,是很难去控制的。

组合命令式

后端服务并不是只有一个,上边这个图中的例子,想要调用很多服务,然后 AB 都过载,它们每一个其实都只是过载一点,通过率可达到 80%,但是前端需要这两个服务的组合服务,那么这里就可能只能达到 60% 的通过率。然后后边如果是更多的服务,那么每个服务的一点点过载,到了前端就是很严重的问题。怎么解决呢?

这本书在 12、13 年的时候很火,里边提到了两个对我们有用的点。

  • 一个是“希望系统是分布式的,去中心化”,指系统过载保护依赖每一个节点自身的情况去做,而不是下达一个统一的中心指令。

  • 二是“希望整个控制是基于反馈的”,它举了一些例子,像抽水马桶,像过去炼钢铁的参数很难配,但是只要有一个反馈机制就好解决了。

于是我们构建了一套看起来有点复杂的过载保护系统。

整个系统基于反馈,然后它把整个 拒绝的信息全程传递 了。看到最右边,有几个典型的服务,从一个 CGI 调用一个后台服务,再调用另一个后台服务,它会在 CGI 层面就把它的重要程度往下传。

回到刚才那个前端调用 A、B 服务的例子,使用这样的一种重要程度传递,就可以直接拒绝那些相同用户的 20% 的请求,这样就解决了这个问题。

怎么配队列?

这个只是反映了 生产者和服务者处理能力的差异,观察这个差异,就可以得到一个好的拒绝的数。你不需要去配它多长,只需要去看一个请求在队列里待的平均时间是否可以接受,是一个上涨趋势还是一个下降趋势。

这样我们就可以决定要不要去拒绝。那这样几乎是全自动的。你只要配得相对大一点就行了,可以抗一些抖动。在接入之前就评估它,在过去一段时间内平均队列耗时多长,如果超过预支,我们就往下调。这样就把整个系统的过载能力提升了很多。

这是一个具体的做法,我们会考虑两个维度,一个是 后台服务,可能服务很多不同的前端,它可能来源于一个支付的请求,经过层层调用,到达后台。

或者是一个 发消息的服务,它也可能是一个不重要的小服务,如果这个账户服务过载的时候,那么我们可以根据这个表来自动地优先去拒绝一些不那么重要的服务请求,使得我们核心服务能力可以更好地提供。这样整个系统就可以做到很好的过载保护。

数据存储

上边提到一个数据层,那我们是怎么去做数据的呢?

在过去很多年里,我们可能是尽可能 去事务化不追求强一致,一般是采用 主备同步 的方法。但我们的目标还是 强一致 的存储。

强一致是说,写一个数据之后,服务器的返回成功不会因为单机故障而丢失。早年我们用的是自己设计的协议,严格来证明的话,没有 Paxos 这么严谨,所以我们在过去一年多的时间内,重新做了一个 Paxos 存储。

它是一个 同步复制 的数据存储,支持各个园区之间的数据一致性,并且是可以多组多写的,就是说任何一个园区接入,它都可以进行数据的强制读写。

另外它并不只是 key-value 模式,它支持 key-value、list、表。在微信这边很少会说完全依赖 key-value 的,因为很多业务都是有列表、表格等的请求,所以很多年前就开始用表格的存储。

Paxos 可用性很高,所以我们就敢做单表有亿行的设计,这样像公众号粉丝等需要很大的,几千万甚至几亿行的记录,就不用考虑自己去分表。并且这个存储可以使用类 SQL 的语句去做,它是完全保证事务的。

它还是插件化系统,不仅支持 LSM,还支持其它存储引擎。

然后它低成本,后台 CPU 有 E3-1230V3,也有 E5-2670 型号的,内存,CPU 与 ssd 之间有一些能力用不上,所以我们系统是可以灵活组合很多不同存储介质的。

这个系统是跑在同城的,也就是上海内部、深圳内部、加拿大内部和香港内部。它们之间的延迟相对较低,几毫秒的级别。这是一个非租约的,没有 leader,不存在切换的不可用期,随时都可以切换任何一个园区。

负载均衡这一块我们沿用 kb64 架构,6 台机为一组。因为园区故障少,平时单机时,分摊 25% 的流量,整体比较稳定。6 台为一组时,整个作为一个 set,有很多 set 之间的适用一致性要去做,会有一个很细粒度的伸缩性,比如它可以 100 组扩展到 101 组。

为什么用这么重的方式?

因为希望应用开发是 简单快速 的,不用假设一个数据写完之后还可能被回退掉,这样会有很多额外的开销,会有很多问题。

比如公众号,他们有很多素材库之类的很重要的存储,如果数据突然丢了,或者说回退了,没有了,那用户投诉是会很严重的。微信账号这边也是这样,如果一个账户注册了,但是这个数据回退了,那也是很严重的问题。

另一个原因是 可用性。在一个传统的主备系统里面,当主机挂掉,面临切不切备机的抉择,然后你会层层请示,说明目前的同步状况,甚至你不知道当前的同步状况,经过很多流程来请示是否切换备机。

而另外,它也不是一个高成本的方案。

为什么不用 Raft?

Raft 的开源很有价值,它把互联网后台的数据一致性能力提升了很多,就算是一个很小的团队,它也能直接用 Raft 获得一个强一致能力,而这可能就已经超过了许多互联网后台的强一致能力,因为很多后台都是用了很古老的架构,比如长期用到主机架构。

Raft 与 Paxos 的区别是什么?

其实 Raft 和 Paxos不是一个层面 的概念,这个图就是典型的通过一个 log 变更 db 的架构,通过三条 log 一致性做到数据持久强一致性。那 Paxos 在哪里?在一个 log 的某一个 entry 上,三个点构成一个常量。

那 Raft 是什么呢?它是整一个二维的东西,就是说,基于一个 Paxos 强一致协议做的一条 log,它整个就是一个 Raft。所以我们可以认为 Raft 其实是 Paxos(log)的一种选择。如果你允许绿色部分不存在,那它就不是 Raft。因为 Raft 的设计是你自己做的,它与 Paxos 没关系。

整个 PaxosStore 架构如图:

它包含了很多层,包括缓存和汇聚层、同步复制的组件等。

这一套方案是在线上用了好几千台的,是一个非租约的方案。存储引擎可以自由定制。如果想用大表,那可以基于 leveldb。如果想用更强的 LSM,也可以选择。然后我们也有很多 Bitcask 的模型,更适合于内存的 key-value。

由于有几万台机,所以变更很重要,我们也基于 BT 做了一套存储方案。它会以园区为根据地,通常一个变更,会以 BT 协议发送到每个园区里,然后园区内部把同机架机器分成一个分组,然后分组内再互传。就我了解,Facebook 和 Twitter、Ebay 都是这样做的。

大系统化小之后,微信如何解决大规模微服务下的难题?相关推荐

  1. 云开发版合成大西瓜小游戏微信小程序源码 微信游戏小程序附带流量主功能

    这是一款云开发版的合成大西瓜小游戏微信小程序源码,微信游戏小程序源码.该小游戏玩法简单,只需要拖动同样的水果落下合成新品众的水果,最终合成大西瓜,玩法酷似俄罗斯方块,相当于换一种形式的俄罗斯方块,简单 ...

  2. 百度商业大规模微服务分布式监控系统——凤睛

    导读:作为凤睛早期的接入方.后期的核心成员,笔者经历了整个项目前后四年的变迁,看过项目的艰难开端.中期的默默积累以及后期的蓬勃发展.每一次架构的变迁都带着技术浪潮的烙印,也看到项目成员利用有限资源来解 ...

  3. 美团大规模微服务通信框架及治理体系OCTO核心组件开源

    来源:美团技术团队 数据猿官网 | www.datayuan.cn 今日头条丨一点资讯丨腾讯丨搜狐丨网易丨凤凰丨阿里UC大鱼丨新浪微博丨新浪看点丨百度百家丨博客中国丨趣头条丨腾讯云·云+社区 微服务通 ...

  4. .NET Core开发实战(第32课:集成事件:解决跨微服务的最终一致性)--学习笔记...

    32 | 集成事件:解决跨微服务的最终一致性 首先看一下集成事件的工作原理 它的目的时为了实现系统的集成,它主要是用于系统里面多个微服务之间相互传递事件 集成事件的实现方式有两种,一种是图上显示的发布 ...

  5. 小技巧收藏之解决Word里9个常见难题

    9个技巧教你解决Word里9个常见难题 1.前言 在生活中,我们经常使用office办公软件,其中做word,基本上只要有工作的,都是需要接触到,很多时候老是有一些莫名其妙的难题,什么方框里面打勾啊, ...

  6. Serverless 时代下大规模微服务应用运维的最佳实践

    微服务架构的优点和痛点 Aliware 1 微服务架构的诞生背景 回到互联网早期时代,也就是web1.0时代,当时主要是一些门户网站,单体应用是当时的主流应用,研发团队相对较小,这时候的挑战在于技术的 ...

  7. 百度商业大规模微服务分布式监控系统-凤睛

    导读:作为凤睛早期的接入方.后期的核心成员,笔者经历了整个项目前后四年的变迁,看过项目的艰难开端.中期的默默积累以及后期的蓬勃发展.每一次架构的变迁都带着技术浪潮的烙印,也看到项目成员利用有限资源来解 ...

  8. 大规模微服务利器:eBPF + Kubernetes

    hi, 大家好,微服务,云原生近来大热,在企业积极进行数字化转型,全面提升效率的今天,几乎无人否认云原生代表着云计算的"下一个时代",IT大厂们都不约而同的将其视为未来云应用的发展 ...

  9. 百度商业系统大规模微服务分布式监控实践

    导 语 作为凤睛早期的接入方.后期的核心成员,笔者经历了整个项目前后四年的变迁,看过项目的艰难开端.中期的默默积累以及后期的蓬勃发展.每一次架构的变迁都带着技术浪潮的烙印,也看到项目成员利用有限资源来 ...

最新文章

  1. 解决注册并发问题并提高QPS
  2. Oracle中的Rowid
  3. JPA(二):HellWord工程
  4. 从万物互联到万物智联,物联网的下一个爆发点在哪里?
  5. [ASP.NET Core 2.0 前方速报].NET Core 2.0.3 已经支持引用第三方程序集了
  6. MATLAB figure中提取数据
  7. kali 安装使用记录
  8. linux 网络瘫痪,Linux内核发现TCP漏洞,小流量也能DoS瘫痪设备
  9. 【JAVA 第三章 流程控制语句】课后习题 计算两个日期之间的天数
  10. 中国区块链相关政策法规演变史
  11. [转]【人是怎么废掉的?】
  12. 我该用 Java 12 还是坚持 Java 11?
  13. (转)如何诊断和解决CPU高度消耗(100%)的数据库问题
  14. cad安装日志文件发生错误_cad安装出现错误 - 卡饭网
  15. epoll中的ET和LT模式区别
  16. jQuery实现打地鼠游戏
  17. [SUCCESS]前后端分离开发-入门案例 +VSCode安装
  18. 【win10操作系统基础】我的电脑 控制面板 win10桌面图标有个箭头如何不显示 去掉win10桌面图标箭头的方法
  19. 【关于为什么要刷力扣的思考】记第二次周赛AK
  20. unity.生成表示地图信息的二维数组_Unity3D 中生成任意形状3D Texture amp; 体积云...

热门文章

  1. 人工智能能否跨越意识鸿沟?
  2. 「长图」使用AI前需要评估的
  3. 多模人车交互,智能汽车的AI感知进化
  4. 人工智能艺术:一场前所未有的新艺术创造
  5. (完全解决)Dataframe的赋值问题SettingWithCopyWarning: A value is trying to be set on a copy of a slice
  6. 前沿速递:因果涌现在多种因果衡量标准下普遍存在
  7. 当思想与机器融合:脑机接口与人类的现在、困境与未来
  8. 杜克大学和Facebook联手开发更好的光通信
  9. 400多家单位、30余万科研人员,10多年奋斗!北斗卫星核心器件实现100%国产!(附:北斗研发建设历程​)...
  10. 6分钟完成ImageNet训练,NVIDIA创下六项AI性能新记录!