请选择 进入手机版 | 继续访问电脑版

默认
打赏 发表评论 3
QQ 18年:解密8亿月活的QQ后台服务接口隔离技术

本文原作者:shane,腾讯后台开发高级工程师。


1、QQ 18年


v2-155ba78f5ba7fd5281362fb4d5ded19f_r.png

1999年2月10日,腾讯QQ横空出世。光阴荏苒,那个在你屏幕右下角频频闪动的企鹅已经度过了18个年头。随着QQ一同成长的你,还记得它最初的摸样吗?

1999年:腾讯QQ的前身OICQ诞生,该版本具备中文网络寻呼机、公共聊天室以及传输文件功能。

1.png
▲ 1999版QQ

2000年,OICQ正式更名为QQ,发布视频聊天功能、QQ群和QQ秀等功能。

2.png
▲ 2000版QQ

2003年版本,QQ发布聊天场景、捕捉屏幕、给好友播放录影及QQ炫铃等功能。  

2004年,QQ新增个人网络硬盘、远程协助和QQ小秘书功能。

几经更迭,QQ版本也产生许多变化,很多操作方式都变了,也让QQ更有现代感了。如今的QQ越来越精美,越来越简洁,如你所见。

3.png
▲ 2009和2010版QQ

4.png
▲ 2013版QQ

据不完全统计,腾讯QQ月活用户达到8.7亿左右,而这个数字还在不断增加......

如此庞大的用户群的任何行为,都会产生巨大的影响。

2017年春节,QQ推出AR红包加入红包大战,经调查手机QQ的红包全网渗透率达到52.9%。

5.png

在此期间,后台又一次承受了海量的压力,请跟着本文来看看腾讯内部对QQ后台的接口处理的相关技术干货,或许可以给到你答案。

2、背景概述


QQ后台提供了一套内部访问的统一服务接口,对腾讯各业务部门提供统一的资料关系链访问服务,后面我们把这套接口简称为DB。

现在说说分set的背景:2013年的某一天,某个业务的小朋友在申请正式环境的DB接入权限后,使用正式环境来验证刚写完的测试程序,循环向DB接口机发送请求包,但因为这个包格式非法,触发了DB解包的一个bug,导致收到这些请求包的服务器群体core dump,无一幸免。。。。整个DB系统的服务顿时进入瘫痪状态。

因此有了故障隔离的需求,2014年初,我们着手DB的故障隔离增强改造。实现方法就是分set服务--把不同业务部门的请求定向到不同的服务进程组上,如果某个业务的请求有问题,最多只影响一个部门,不会影响整个服务系统。

3、总体方案


为了更清楚描述分set的方案,我们通过两个图进行分set前后的对比。

分set之前:
6.png

分set之后:
7.png

从图中可以看出,实现方式其实非常简单,就是多启动一个proxy进程根据IP到set的映射关系分发请求包到对应set的进程上。

4、分set尝试


很多事情往往看起来非常简单,实现起来却十分复杂,DB分set就是一个典型的例子。怎么说呢?先看看我们刚开始实现的分set方案。

实现方案一:通过socket转包给分set进程,分set进程直接回包给前端。

8.png

这个方案刚发布几台后就发现问题:

  • 1)有前端业务投诉回包端口不对导致访问失败。后来了解这些业务会对回包端口进行校验,如果端口不一致就会把包丢弃;
  • 2)CPU比原来上涨了25%(同样的请求量,原来是40%,使用这个方案后CPU变成50%)。

回包端口改变的问题因为影响业务(业务就是我们的上帝,得罪不起^^),必须马上解决,于是有了方案二。

实现方案二:通过socket转包给分set进程,分set进程回包给proxy,由proxy回包。

9.png

改动很快完成,一切顺利,马上铺开批量部署。

晚上10点准时迎来第一次高峰,DB出现大量的丢包和CPU告警,运维紧急迁移流量。第二天全部回滚为未分set的版本。

重新做性能验证的时候,发现CPU比原来涨了50%,按这个比例,原来600多台机器,现在需要增加300多台机器才能撑起同样请求的容量。(这是写本文时候的机器数,目前机器数已经翻倍了~)

后来分析原因的时候,发现网卡收发包量都涨了一倍,而CPU基本上都消耗在内核socket队列的处理上,其中竞争socket资源的spin_lock占用了超过30%的CPU -- 这也正是我们决定一定要做无锁队列的原因。

5、最终实现方案


1方案概述


做互联网服务,最大的一个特点就是,任何一项需求,做与不做,都必须在投入、产出、时间、质量之间做一个取舍。

前面的尝试选择了最简单的实现方式,目的就是为了能够尽快上线,减少群体core掉的风险,但却引入了容量不足的风险。

既然这个方案行不通,那就得退而求其次(退说的是延期,次说的是牺牲一些人力和运维投入),方案是很多的,但是需要以人力作为代价。

举个简单的实现方法:安装一个内核模块,挂个netfilter钩子,直接在网络层进行分set,再把回包改一下发送端口。

这在内核实现是非常非常简单的事情,但却带来很大的风险:

  • 1)不是所有同事都懂内核代码;
  • 2)运营环境的机器不支持动态加载内核模块,只能重新编译内核;
  • 3)从运维的角度:动内核 == 杀鸡取卵 -- 内核有问题,都不知道找谁了。

好吧,我无法说服开发运营团队,就只能放弃这种想法了--即便很不情愿。

跑题了,言归正传,这是我们重新设计的方案:
10.png

方案描述:

  • 1)使用一写多读的共享内存队列来分发数据包,每个set创建一个shm_queue,同个set下面的多个服务进程通过扫描shm_queue进行抢包;
  • 2)Proxy在分发的时候同时把收包端口、客户端地址、收包时间戳(用于防滚雪球控制,后面介绍)一起放到shm_queue中;
  • 3)服务处理进程回包的时候直接使用Raw Socket回包,把回包的端口写成proxy收包的端口。

看到这里,各位同学可能会觉得这个实现非常简单。不可否认,确实也是挺简单的。

不过,在实施的时候,有一些细节是我们不得不考虑的,包括:

  • 1)这个共享内存队列是一写多读的(目前是一个proxy进程对应一组set化共享内存队列,proxy的个数可以配置为多个,但目前只配一个,占单CPU不到10%的开销),所以共享内存队列的实现必须有效解决读写、读读冲突的问题,同时必须保证高性能;
  • 2)服务server需要侦听后端的回包,同时还要扫描shm_queue中是否有数据,这两个操作无法在一个select或者epoll_wait中完成,因此无法及时响应前端请求,怎么办?
  • 3)原来的防滚雪球控制机制是直接取网卡收包的时间戳和用户层收包时系统时间的差值,如果大于一定阀值(比如100ms),就丢弃。现在server不再直接收包了,这个策略也要跟着变化。

2基于signal通知机制的无锁共享内存队列


A. 对于第一个问题,解决方法就是无锁共享内存队列,使用CAS来解决访问冲突:
11.jpg

这里顺便介绍一下CAS(Compare And Swap),就是一个汇编指令cmpxchg,用于原子性执行CAS(mem, oldvalue, newvalue):如果mem内存地址指向的值等于oldvalue,就把newvalue写入mem,否则返回失败。

那么,读的时候,只要保证修改ReadIndex的操作是一个CAS原子操作,谁成功修改了ReadIndex,谁就获得对修改前ReadIndex指向元素的访问权,从而避开多个进程同时访问的情况。

B. 对于第二个问题,我们的做法就是使用注册和signal通知机制:
12.jpg

工作方式如下:

  • 1)Proxy负责初始化信号共享内存;
  • 2)Server进程启动的时候调用注册接口注册自己的进程ID,并返回进程ID在进程ID列表中的下标(sigindex);
  • 3)在Server进入睡眠之前调用打开通知接口把sigindex对应的bitmap置位,然后进入睡眠函数(pselect);
  • 4)Proxy写完数据发现共享内存队列中的块数达到一定个数(比如40,可以配置)的时候,扫描进程bitmap,根据对应bit为1的位取出一定个数(比如8,可以配置为Server进程的个数)的进程ID;
  • 5)Proxy遍历这些进程ID,执行kill发送信号,同时把bitmap对应的位置0(防止进程死了,不断被通知);
  • 6)Server进程收到信号或者超时后从睡眠函数中醒来,把sigindex对应的bit置0,关闭通知。

除了signal通知,其实还有很多通知机制,包括pipe、socket,还有较新的内核引入的eventfd、signalfd等等,我们之所以选择比较传统的signal通知,主要因为简单、高效,兼容各种内核版本,另外一个原因,是因为signal的对象是进程,我们可以选择性发送signal,避免惊群效应的发生。

4防滚雪球控制机制


前面已经说过,原来的防滚雪球控制机制是基于网卡收包时间戳的。但现在server拿不到网卡收包的时间戳了,只能另寻新路,新的做法是:

Proxy收包的时候把收包时间戳保存起来,跟请求包一起放到队列里面,server收包的时候,把这个时间戳跟当前时间进行对比。

这样能更有效的做到防滚雪球控制,因为我们把这个包在前面的环节里面经历的时间都考虑进来了,用图形描述可能更清楚一点。

13.jpg

6、性能验证


使用shm_queue和raw socket后,DB接口机处理性能基本跟原来未分set的性能持平,新加的proxy进程占用的CPU一直维持在单CPU 10%以内,但摊分到多个CPU上就变成非常少了(对于8核的服务器,只是增加了1.25%的平均CPU开销,完全可以忽略不计)。

最后,分set的这个版本已经正式上线运行一段时间了,目前状态稳定。

7、本文小结


对许多企业而言,虽不一定经历月活8亿用户,但为了能够面对蜂拥而来的用户游刃有余,时刻了解并保持自己的最优状态迎接用户,一定要在上线之前对自己的网站承载能力进行一个测试。

(原文链接:点此进入

附录:更多有关微信、QQ的文章


[1] 有关QQ、微信的技术文章:
QQ 18年:解密8亿月活的QQ后台服务接口隔离技术
月活8.89亿的超级IM微信是如何进行Android端兼容测试的
以手机QQ为例探讨移动端IM中的“轻应用”
一篇文章get微信开源移动端数据库组件WCDB的一切!
微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化
微信后台基于时间序的海量数据冷热分级架构设计实践
微信团队原创分享:Android版微信的臃肿之困与模块化实践之路
微信后台团队:微信后台异步消息队列的优化升级实践分享
微信团队原创分享:微信客户端SQLite数据库损坏修复实践
腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率
腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)
腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)
微信Mars:微信内部正在使用的网络层封装库,即将开源
如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源
开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]
微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解
微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)
微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]
微信团队原创分享:Android版微信从300KB到30MB的技术演进
微信技术总监谈架构:微信之道——大道至简(演讲全文)
微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]
如何解读《微信技术总监谈架构:微信之道——大道至简》
微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]
微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案
微信朋友圈海量技术之道PPT [附件下载]
微信对网络影响的技术试验及分析(论文全文)
一份微信后台技术架构的总结性笔记
架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]
快速裂变:见证微信强大后台架构从0到1的演进历程(一)
快速裂变:见证微信强大后台架构从0到1的演进历程(二)
微信团队原创分享:Android内存泄漏监控和优化技巧总结
全面总结iOS版微信升级iOS9遇到的各种“坑”
微信团队原创资源混淆工具:让你的APK立减1M
微信团队原创Android资源混淆工具:AndResGuard [有源码]
Android版微信安装包“减肥”实战记录
iOS版微信安装包“减肥”实战记录
移动端IM实践:iOS版微信界面卡顿监测方案
微信“红包照片”背后的技术难题
移动端IM实践:iOS版微信小视频功能技术方案实录
移动端IM实践:Android版微信如何大幅提升交互性能(一)
移动端IM实践:Android版微信如何大幅提升交互性能(二)
移动端IM实践:实现Android版微信的智能心跳机制
移动端IM实践:WhatsApp、Line、微信的心跳策略分析
移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)
移动端IM实践:iOS版微信的多设备字体适配方案探讨
信鸽团队原创:一起走过 iOS10 上消息推送(APNS)的坑
腾讯信鸽技术分享:百亿级实时消息推送的实战经验
>> 更多同类文章 ……

[2] 有关QQ、微信的技术故事:
技术往事:创业初期的腾讯——16年前的冬天,谁动了马化腾的代码
技术往事:史上最全QQ图标变迁过程,追寻IM巨人的演进历史
技术往事:“QQ群”和“微信红包”是怎么来的?
开发往事:深度讲述2010到2015,微信一路风雨的背后
开发往事:微信千年不变的那张闪屏图片的由来
开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)
一个微信实习生自述:我眼中的微信开发团队
首次揭秘:QQ实时视频聊天背后的神秘组织
>> 更多同类文章 ……

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:腾讯手机管家iOS版的iPhoneX适配实践分享下一篇:微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉

本帖已收录至以下技术专辑

推荐方案
评论 3
都能感受到bug出现时那种气氛
引用:tuna 发表于 2017-10-13 11:15
都能感受到bug出现时那种气氛

头像是本人吗
签名: 《保活终极总结(三):Android6.0及以上的保活实践(被杀复活篇)》http://www.52im.net/thread-1140-1-1.html
引用:JackJiang 发表于 2017-10-13 13:28
头像是本人吗

必须的
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

Processed in 0.171877 second(s), 40 queries , Gzip On.

返回顶部