海量请求,对互联网企业而言往往是悲喜交加的事情:既期待有海量的用户来支撑自己的雄心壮志,又害怕海量的请求将自己的服务击垮。如何去平衡这种矛盾,如何进入一个用户与技术互相促进增长的正循环,相信众多即使已经拥有丰富经验的互联网巨头仍会思考这个问题。
2016年7月15-16日, ArchSummit全球架构师峰会 将在深圳举行。本届大会,我们邀请了腾讯高级工程师徐汉彬,前来分享 《如何应对节日高峰—QQ会员活动运营系统架构实践》 的内容,讲述的是如何通过高并发和高可用方面的综合优化和实践,构建了一个满足活动业务、支撑海量请求的平台架构。
我们借此机会采访了徐汉彬老师,他分享了从各方面对抗海量请求的细节与故事。
受访嘉宾介绍:
徐汉彬,腾讯高级工程师(SNG增值产品部),QQ会员体系合作线技术团队负责人。曾就职于阿里巴巴、小满科技。QQ会员活动运营平台(AMS)的初始开发,在四年多的时间里,将该平台从日请求百万级升级至亿级(日请求3亿-8亿),负责该平台的架构设计和研发建设,在Web系统架构方面拥有比较丰富的实践和积累。
InfoQ: 能否介绍您在腾讯的工作职责?
徐汉彬:目前,我在SNG增值产品部担任高级工程师,会员体系合作线技术团队和AMS平台的负责人,主要负责这两块的研发工作。QQ会员生活特权(合作线)偏向于业务工作,而AMS平台则是部门的核心基础系统,偏向于纯研发工作。
InfoQ: QQ会员活动运营平台(以下简称AMS)每日接受用户触发的Web请求达到3亿-8亿,这个流量波动是否较大,AMS是如何应对的?
徐汉彬:目前,AMS同时在线的活动项目是800多个,因为在线的项目比较多而且归属不同的业务(会员体系、腾讯游戏、个性化、动漫、阅读等等),每个业务各自推广时间在非节假日通常不会聚集,还算比较平稳。但是在典型节日、重要秒杀类活动时,流量的变化波动比较大,尤其是请求峰值甚至是平时的7倍以上,会达到7w/s。
另外,有一些流量峰值的到来是在我们意料之外的,因为同时在线的活动项目比较多,某个活动忽然做推广有时根本没有通知到我们,在这方面我们主要遇到两个方面的问题:
系统的各个链路和Server必须具备快速扩容和缩容的能力;整个系统的架构必须支持快速扩容,也就是说各个核心的环节都必须具备快速平行扩容的能力。我们的后台Server目前都是采用无状态、L5名字服务方式接入到系统内,这些Server通常都是支持平行扩容。
在扩容工具支持方面,我们公司也有比较充分的运维工具支持,Server的环境搭建、部署、安装和启动都实现了高度自动化。
但是我们在2015年早期仍然做不到快速扩容,原因在于:AMS是活动运营系统,内部涉及很多的高价值礼包发货接口,这些接口和我们的服务之间通常会做签名加密校验和来源IP限制,在我们扩容机器的时候需要新增机器IP,而这些是需要发货接口负责人工审批或者手动添加IP白名单的。因此在我们以前的扩容流程里,通常需要耗费1天以上的时间主要找外部门的发货接口负责人进行新IP的人工审批或者操作。
我们后来的解决方案就是在和这些敏感发货接口之间搭建了自己统一的加密Proxy Server,收拢了IP。于是只要我们的Proxy Server不进行扩容,就不需要专门找接口负责人审批,而在平台内部,实现自己内部的签名加密校验和IP来源限制,将授权从外部授权变为内部授权。我们后来将机器扩容的耗时,从原来1天以上大幅度缩短到1-2个小时,实现了我们的快速扩容能力以应对流量突增。
如何避免持有的机器过多而导致在非节假日的机器资源浪费活动运营系统有一个持有机器的成本问题:持有太多,非节假日因为流量比较小而资源浪费,运维部门也会挑战我们的机器成本问题;持有太少,节假日的高峰又有过载的风险,因为峰值突增波动很大。
虽然我们具备快速扩容和缩容的能力,但是通常来说,我们需要尽量避免现网的服务机器的频繁变更,尽量保持现网服务的稳定才是我们比较期望的。
因此AMS系统大部分的机器,都采用运维部门提供的Linux Container虚拟化的虚拟机,使用它的共享CPU的能力。假设一台24核的物理母机分为8台虚拟子机,然后进行业务混部(8台子机都部署了不同的业务服务),AMS占据其中一台子机器,AMS的服务可以高优先级使用1/8的CPU资源,这在非节假日这已经足够了;然后到了节假日,1/8的CPU资源不够用,可以申请另外7台子机器的空闲CPU资源,获得更多CPU资源。
因为常规产品服务的流量通常变化趋势是比较稳定,占据的CPU资源也是比较稳定的,不会像活动系统那样流量的波动非常大。这种共享CPU的虚拟化技术非常适合活动运营的模式,平时不至于浪费机器资源,节假日又可以突破1/8的CPU使用上限,将其他业务的空闲资源充分利用起来。
InfoQ: Web系统的高并发优化和实践遇到最大的挑战是什么,如何解决的?
徐汉彬:因为我们是活动运营平台,AMS接入了数以百计的第三方接口。以游戏为例子,我们接入了160款PC端游戏和手游,每一个游戏就是一个独立的游戏服务系统,并且这些接口的性能、延时都参差不齐。在高并发场景下,如何避免被后端各类接口“挂住”是最大的挑战之一,因为如果后端接口超时,会导致Web Server或负责调用的Server陷入漫长的等待,一直等待到超时结束为止。
等待状态本质上是系统各个链路上的系统资源被占据和消耗的过程,这个过程如果越短,就相当于越节约各个链路上的系统资源。因为只有请求被处理完毕,链路上的资源才能被释放出来提供给其他新请求继续使用。如果这类型的“超时等待”过多,甚至会将Server所有可用的资源全部占据,最终导致正常的请求也没有服务资源可以使用。
关于网络I/O的资源等待问题,部分同学可能会提出疑问:Server实现异步化不就可以解决这个问题吗?实际上我们的后台Server大部分都实现了异步化的,异步化的实现确实不会出现网络阻塞场景,但是其他资源仍然会被占据。例如,Server在处理A任务时遇到阻塞,迅速切去处理B任务,但是A任务的相关数据我们需要“保留现场”,它占据的内存、句柄资源等都仍然不能被释放出来提供给其他请求使用。另外,目前我们Web Server层的实现仍然采用了同步阻塞的通信模式,而我们采用的规避方式包括:
根据各个接口的延时轻重分离,支持设置动态的超时时间,尽可能将超时时间设置得相对合理且又比较短,尽量减少等待时长;
优化服务的单机性能,简单的说就是期望使用更少的系统开销(CPU、内存等)来支持更高的并发数。Web Server升级到Apache2.4+PHP7(Event),而涉及第三方外部通信的后端Server采用协程序(微线程)实现,让这些Server的并发能力更强,即使发生了部分第三方接口全面超时的场景,仍然有比较可观的支撑能力;
实现流量控制的过载保护,防止第三方的接口因为过载而直接挂掉,进而导致接口全面超时和不可用。我们认为:虽然流量限制模式会主动拒绝一些用户请求,但与整个服务不可用相比仍然是相对比较好的一个体验。
InfoQ: 海量请求下后端服务因为资源不足而陷入异常,为了保证用户体验,需要优先保护哪些用户行为,对应背后的技术操作是如何实现的?
徐汉彬:后端服务因为资源不足而陷入异常,我们会优先保证 活动参与的逻辑流程相关的环节服务 ,例如活动配置读取、计数器、登录态等等,而非核心的则是安全保护、常规流水、数据上报等。
对于非核心操作,根据不同的功能类型我们采用不同的技术实现。例如数据上报,我们通常设置了一个比较短的超时时间(30ms),并且不管这个操作执行结果成功还是失败,程序都继续往下走。假如这个服务异常,最多影响整个流程的30ms,而前端响应增加几十毫秒对用户来说几乎是感知不到的,这种类型的技术实现非常简单;
对于安全监测服务,我们的做法相对就复杂一些:我们搭建一个缓存周期为12个小时的Cache服务,如果安全检测Server异常了,我们就根据Cache来做安全监测。如果Cache也挂了,那就放弃安全监测,让用户直接通过。
在一些重要的活动中,例如2016年的春节红包活动,我们采用集群轻重分离的模式,非核心的请求独立搭建一个独立集群,核心的发货功能搭建一个机器资源冗余更多的独立集群,彼此之间物理隔离。如果流量过大,我们尽量保证核心功能不受到影响。
InfoQ: 在海量请求中有多少是无用重复甚至是恶意的流量?能否结合谈谈你们的安全策略,如何保护推广的活动资源?
徐汉彬:在这方面,我们拥有比较丰富的经验,因为我们运营平台每天发货数量达到6000万以上,里面涉及数量不少的高价值礼包,包括实物、Q币、高价值虚拟物品等。我们为了保护这些高价值的资源安全搭建了比较完整的安全体系,其中在拦截恶意流量方面做了很多的实践:
首先就是比较常规的 IP和账号黑名单拦截 ,这个主要是由公司安全部门提供的拦截能力。其次还有我们联合手Q终端共同建设起来的 独特而多维度的拦截策略 ,包括加密协议通信、手机特征码绑定和Android虚拟机拦截等:
加密协议通信对于我们的核心活动,我们的Web Server会拒绝来自HTTP的请求,通过手Q终端,系统只相信来自加密协议的请求。因为HTTP是一个公开透明的应用层传输协议,对于恶意用户来说可以非常低成本地通过脚本实现请求,而采用我们私有的加密协议通信就可以拒绝掉这类型的自动化脚本类型的恶意流量;
手机特征码绑定策略实际上业界使用得也比较广泛,简单的说就是一个账号绑定一台手机(我们支持可动态配置),如果两者绑定关系不一致就拦截请求;
Android虚拟机拦截利用手机硬件特征信息和数据,通过我们的检测规则分析该请求是否来自Android虚拟机从而进行拦截。
我们建设了多重安全保护门槛,形成体系的安全保护能力来拦截恶意流量。
InfoQ: AMS从以前的PHP5.2+Apache2.0升级至PHP7+Apache2.4,这种偏底层的升级是否危险,从预研到全量升级经历怎样的过程?是否建议其他互联网公司也进行升级,为什么?
徐汉彬:对于一个流量规模比较大的在线服务,去升级它的基础软件环境,尤其是升级到比较新的版本其风险是比较大的,而且通常基础研发升级的挑战比较大,因为升级做得好,业务不一定感知得到,但是如果升级过程出了问题从而影响了线上业务,则需要承担比较重的事故责任。而且AMS上承载了很多业务营收相关的活动,更需要做到升级过程中的风险可控。
经过我们对PHP7和新版本Apache的学习和分析,我们评估认为PHP7在性能方面有大幅度的提升,以及Apache2.4的Event模式对高并发场景有更强的支持。这个升级方案有利于节省我们的机器成本,并且让系统的支撑能力更强,值得我们实施,因此我们决定推进整个基础软件项目计划的升级。
从我们升级后的线上机器负载、压测等数据看,也的确达到了我们升级前的预期,我们单机QPS比原来提升了3倍以上,并且从老Apache原来的Prefork(多进程模式)升级到新Apache的Event模式(多进程多线程模式),单机并发能力也明显提高。
把控风险方面,除了比较充分的功能测试之外,我们还采用了 循序渐进的分阶段的灰度和观察策略 :非核心功能集群的单机灰度-->非核心功能集群-->其他功能集群。在这个过程中也确实可以发现不少的问题,所以这也是一个问题发现和修复的过程。在灰度一段时间后,相关问题修复得差不多,在观察比较稳定的场景下,我们就会进一步扩大升级范围继续观察。
至于其他有使用PHP的互联网公司是否需要升级到PHP7的问题,要根据实际的业务场景和特性而定。因为技术升级方案的选择不仅需要考虑当前业务的实际瓶颈和问题,也要充分考虑投入产出比。
通常来说,我们要选择适合业务的技术方案,而非“最佳”的技术方案,只有满足业务当前和未来的发展需求才是技术方案的真正价值和目的所在。
InfoQ: 您在博客上积累了大量技术文章,您是如何培养这个习惯的?这个习惯给你生活和工作带来了怎样的影响?
徐汉彬:我写技术博客,其实是对自身过往一段时间里的技术实践的一种思考和总结,写技术文章的过程就是对自己技术学习和实践的一种反思,是一种非常有益处的习惯。如果大家都带着总结技术实践的心态去写技术博客,相信大家会有更有动力。
一般来说,大量应用实践的总结可以获得丰富的经验,而丰富的经验进一步提炼和抽象,就有可能形成方法论。总结和反思的过程,能够让我以更全面和广阔的思维角度看待自己过往的技术实践,更容易让自己看见可以改进和做得不足的环节。
另外这些技术文章分享出来也让我认识了更多志同道合的朋友,获得更多的技术学习和交流的机会,也是一件有价值并且令人开心的事。互联网的技术发展是比较快的,技术的学习和实践不能闭门造车,充分的外部交流也是很有意义的,写技术文章其实也是交流的一种形式,促进思考,相互启发。
InfoQ:感谢徐汉彬老师接受我们的采访。期待您在 ArchSummit全球架构师峰会 上的分享。
更多内容请关注微信公众号:it_haha