QQ18年,解密8亿月活的QQ后台服务接口隔离技术

时间:2022-09-11 17:18:49

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

QQ18年

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

1999年QQ界面

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

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

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

···

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

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

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

在此期间,后台想必又一次承受了海量的压力,年后第一波推送,来看看腾讯内部对QQ后台的接口处理的相关技术干货,或许可以给到你答案。


一、背景

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

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

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

二、总体方案

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

分set之前:

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

 

 分set之后:

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

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

三、分set尝试

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

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

1,有前端业务投诉回包端口不对导致访问失败。后来了解这些业务会对回包端口进行校验,如果端口不一致就会把包丢弃。

2,CPU比原来上涨了25%(同样的请求量,原来是40%,使用这个方案后CPU变成50%)

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

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

晚上10点准时迎来第一次高峰,DB出现大量的丢包和CPU告警,运维紧急迁移流量。

第二天全部回滚为未分set的版本。

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

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

四、最终实现方案

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

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

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

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

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

1,不是所有同事都懂内核代码

2,运营环境的机器不支持动态加载内核模块,只能重新编译内核

3,从运维的角度:动内核 == 杀鸡取卵 -- 内核有问题,都不知道找谁了

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

方案描述:

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不再直接收包了,这个策略也要跟着变化。

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

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

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

工作方式如下:

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,避免惊群效应的发生。

防滚雪球控制机制

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

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

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

QQ18年,解密8亿月活的QQ后台服务接口隔离技术

五、性能验证

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

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


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

腾讯提供了一个可以自主进行服务器性能测试的环境,用户只需要填写域名和简单的几个参数就可以获知自己的服务器性能情况,目前在腾讯WeTest平台可以免费使用。

腾讯WeTest服务器性能测试运用了沉淀十多年的内部实践经验总结,通过基于真实业务场景和用户行为进行压力测试,帮助游戏开发者发现服务器端的性能瓶颈,进行针对性的性能调优,降低服务器采购和维护成本,提高用户留存和转化率。

功能目前免费对外开放中,点击 http://WeTest.qq.com/gaps 即可体验!

如果对使用当中有任何疑问,欢迎联系腾讯WeTest企业qq:800024531

QQ18年,解密8亿月活的QQ后台服务接口隔离技术的更多相关文章

  1. 5天2亿活跃用户,QQ“LBS+AR”天降红包活动后台揭密

    作者:Dovejbwang,腾讯后台开发工程师,参与“LBS+AR”天降红包项目,其所在“2016春节红包联合项目团队”获得2016公司级业务突破奖. 商业转载请联系腾讯WeTest获得授权,非商业转 ...

  2. Tumblr:150亿月浏览量背后的架构挑战

    Tumblr:150亿月浏览量背后的架构挑战 2013/04/08 · IT技术, 开发 · 9.9K 阅读 · HBase, Tumblr, 架构 英文原文:High Scalability,编译: ...

  3. 微软全球资深副总裁对 VS Code 黑宝书的推荐序!VS Code 月活用户已达 1200 万!

    前不久,首本 VS Code 中文书终于问世了! 在本书出版之前,我很高兴能邀请到微软全球资深副总裁 Julia Liuson 为本书写推荐序!下面,我们就来看一下 Julia 所写的推荐序的完整内容 ...

  4. 微信用户量破6.5亿 首超移动QQ

    腾讯控股有限公司昨日公布了微信和WeChat合并月活跃用户量达到6.5亿,同比再涨39%.QQ在移动智能终端月活为6.39亿,同比增长18%,尽管势头也不错,但这是第一次,微信干掉了移动QQ! 201 ...

  5. 陈思淼:阿里6个月重写Lazada,再造“淘宝”的技术总结

    小结: 1. 所谓的中台技术,就是从 IDC,网络,机房,操作系统,中间件,数据库,算法平台,数据平台,计算平台,到业务平台,每一层都有清晰的定义和技术产品. 具体来看,首先,集团技术的分层和每层的产 ...

  6. QQ亿级日活跃业务后台核心技术揭秘

    http://ms.csdn.net/geek/75478 引言 作为本次技术开放日后台架构专场的出品人我今天给大家带来<构造高可靠海量用户服务-SNG数亿级日活跃业务后台核心技术揭秘>, ...

  7. 我花了2个月时间,整理了100篇Linux技术精华,技术人必看

    一个以技术为立身根基的教育机构做出来的微信号,干货程度会有多高? 马哥Linux运维公众号运营五年,从一开始的定位就是给技术人分享加薪干货的地方.这五年里,公众号运营最重的任务就是做内容.内容并不好做 ...

  8. 9月16日,base 福州,2018MAD技术论坛邀您一起探讨最前沿AR技术!

    “ 人工智能新一波浪潮带动了语音.AR等技术的快速发展,随着智能手机和智能设备的普及,人机交互的方式也变得越来越自然. 9月16日,由网龙网络公司.msup联合主办的MAD技术论坛将在福州举行.本次论 ...

  9. 1月中旬值得一读的10本技术新书(机器学习、Java、大数据等)!

    1月中旬,阿里云云栖社区 联合 博文视点 为大家带来十本技术书籍(机器学习.Java.大数据等).以下为书籍详情,文末还有福利哦! 书籍名称:Oracle数据库问题解决方案和故障排除手册 内容简介 & ...

随机推荐

  1. &lbrack;&period;net 面向对象编程基础&rsqb; &lpar;19&rpar; LINQ基础

    [.net 面向对象编程基础] (19)  LINQ基础 上两节我们介绍了.net的数组.集合和泛型.我们说到,数组是从以前编程语言延伸过来的一种引用类型,采用事先定义长度分配存储区域的方式.而集合是 ...

  2. Emmet语法大全手册

    这是基于官方手册整理制作的,因为那个手册网页打开很慢,所以就整理在这里了.以备不时之需. Syntax   Child: > nav>ul>li <nav> <ul ...

  3. 安装CentOS

    1. 用UltraISO,将CentOS写入U盘,然后将两个CentOS iso文件也拷贝到u盘中,由于u盘FAT32的限制,需要调整第一个iso文件的尺寸,剪切到4GB以内即可拷贝进u盘 2. 用u ...

  4. VM安装失败

    出现:VMware安装失败 “Failed to create the requested registry key Key:installer Error:1021" 解决: 开始--运行 ...

  5. 【Todo】深入PHP内核系列

    看到一个<深入PHP内核>系列,Todo: http://www.csdn.net/article/2014-09-15/2821685-exploring-of-the-php [问底] ...

  6. linux 文件查找和压缩工具

    文件查找 1,which命行查找可执行文件,which 只会搜索系统$PATH目录 2,whereis,查找可执行文件,并显示出此文件的man page文件,并且可以查找到系统的库目录 3,locat ...

  7. input的type属性

    input的type属性:http://www.w3school.com.cn/tags/att_input_type.asp 基本语法: <input type="hidden&qu ...

  8. 关于MTK平台SIM-ME Lock的配置方案

    针对一些运营商的锁网需求,MTK平台已经对其有很好的支持.绝大多数的海外需求可以通过直接配置相关文件来完成.这里简单描述一下配置方法,不做原理分析. 相关数据结构分析: Modem中与SML锁网配置相 ...

  9. python sys&period;modules模块

    sys.modules是一个全局字典,该字典是python启动后就加载在内存中.每当程序员导入新的模块,sys.modules都将记录这些模块.字典sys.modules对于加载模块起到了缓冲的作用. ...

  10. jQuery formValidator API

    jQuery formValidator插件的API帮助 目前支持5种大的校验方式,分别是:inputValidator(针对input.textarea.select控件的字符长度.值范围.选择个数 ...