11.11大促,随着移动业务量的激增,小米推送等基础服务也经受了巨大考验。11月12日,小米项目负责人王轩然在微博上宣布,“小米推送服务累计发送消息9.65亿条,平均每分钟67万条消息。更值得一提的是,后台监控显示,推送服务后台系统全天运行非常稳定,没有任何卡顿和拥堵,让各种促销、返利、订单更新消息第一时间到达用户手中。”王轩然,2007年毕业于清华大学计算机系,后加入微软亚洲工程院,参与开发WP7浏览器。2010年7月加入小米,曾任小米聊天安卓团队负责人。现为小米项目总监,负责小米开发者服务,分管推送服务、统计服务、移动广告联盟三大业务,旨在为小米打造一个平台。移动应用程序业务的互联网生态系统。我们联系上了王轩然,就小米推送服务的架构、特性、性能等问题对他进行了采访。以下内容基于此次采访。基础技术架构协议是推送服务的核心。小米推送服务采用的协议是从之前的小米聊天演变而来的,小米聊天一开始就选择使用XMPP协议,随后开发团队对XMPP协议进行了多轮精简和重构。现在XMPP部分只是作为数据传输层,各种独立的服务运行在上面。每个服务称为一个“通道”;每个通道上运行的数据格式可以不同。消息推送服务是其中一个通道,通过Thrift在这个通道上传输的数据是二进制协议格式。我们来看看小米推送服务的服务端架构。下图是后台服务器的基本架构图。整个服务器包括以下几层:XMPP前端:用于维护与客户端的长期连接,使用EJabberd工程处理来自客户端的XMPP请求,使用XMQ模块处理客户端特有的XMPP消息协议推送服务。中间层:业务逻辑层,主要用于异步消息请求,创建和维护消息队列,处理客户端的一些命令请求(注册,设置别名,设置主题等)。HTTP前端:该层负责连接第三方App服务端发送消息的HTTPS请求,以及客户端生成的账号的HTTPS请求。然后是数据存储,这里使用了小米统一的HBase存储,也使用了MySQL把一些数据(topic等)层Redis作为缓存。***查看客户端架构。客户端SDK主要包括两层:SDK层和PushService层。前者提供了App访问的接口,回调方法,以及反序列化Thrift数据的处理逻辑;后者用于维护XMPP持久连接和收发消息。Intent方法用于在两层之间传输数据。值得一提的是,在MIUI系统上,PushService层是系统共享的,即MIUI系统提供了统一的PushService管理模块,不需要每个应用单独启动自己的PushService。功能实现小米推送服务支持单条消息和群消息两种推送方式。单个消息同时支持regID和别名。regID是小米推送服务后台根据设备ID+appID+时间戳生成的。为了降低设备碰撞的概率,我们使用的设备ID是imei+AndroidID+build序列号。别名由App在客户端设置中上报,方便App将自己的设备/用户标识与我们的regID关联起来,这样App就不需要维护regID与设备/用户的对应关系在后台。群组消息通过标记来区分。客户端和服务器都可以为指定的设备设置标签。发送消息时,只需要选择指定的标签发送,小米推送后台会展开标签对应的设备。标签支持的设备数量没有限制。如何保证小米推送服务的稳定性?小米推送服务采用多机房方案,平时流量均摊。一旦某个机房发生故障,流量将无缝切换到其他机房,单个机房的容量可以保证无损提供服务。目前部署在两个机房,预计明年扩建第三个机房。安全性也是小米推送服务的一个重要考虑因素。在数据传输过程中,得益于推送服务采用的双层协议方案,消息会被双重加密,最重要的是XMPP传输层,保证数据在传输过程中不被篡改和监听网络传输过程。二是在Thrift二进制层,保证消息到达Service后,通过广播发送给App进程的过程中不会被拦截和伪造。二次加密往往被其他第三方推送服务忽略,但风险也很大。性能指标11.11提升,面对的请求量在小米推送服务的设计能力之内,目前的设计和机器规模可以支持每分钟1000万条消息的峰值;平时业务量至少40万条/分钟,峰值600万条/分钟。推送消息量通常波动较大,开发团队随时准备流量可能突然暴涨200%的情况,在线下进行压力测试和优化;如果流量特别大,有以下对策:异步队列处理,此时消息传递时间可能比平时慢,但不会对整个系统造成太大影响;消息具有优先级,广播消息将以低优先级处理;限流,控制开发者发送消息的频率;扩容,如果机器负载过高或某项服务出现瓶颈,可以快速添加机器,部署服务,增强系统处理能力。小米推送服务经历的重构软件系统,在发展演进过程中,经常会经历大规模的重构。小米推送服务有过两次比较大的重构。一是将开发语言从Erlang改为Java。小米原来的消息系统是用Erlang开发的,所以最新版的推送系统也是基于Erlang的;但Erlang社区不够活跃,开发者难找,学习曲线陡峭,配套工具和类库少,所以后来发展起来的。团队选择使用Java进行二次开发;迁移到Java后,对开发人员的要求降低了,工具和库也多了,大大提高了开发效率。二是无处不在的Cache。客户端使用小米推送服务的SDK,开发者使用API??的情况千变万化,很多场景都是意想不到的;需要对频繁调用的业务加一个Cache,尽量在本地进程处理;例如,对于客户端调用API设置别名和主题订阅,首先检查是否设置了Cache,如果没有设置才发送给后端服务;优化后,后台服务的业务压力大大降低。小米推送服务开发过程中的一些感悟,应该支持水平扩展,尽可能无状态,或者使用一致性哈希进行划分;便于扩展,保证即使系统暂时出现性能瓶颈,也可以通过增加机器来解决。监控先行,可以轻松采集和分析服务器负载和业务请求量、百分位、慢日志,可以清楚地了解系统的瓶颈,并有针对性地进行改进。不要过早优化,先实现功能尽快上线,根据监控数据对重点区域进行优化。敏捷开发,快速迭代,天天都有,每天都有简短的站会,可以快速响应变化,不断完善系统。
