作者简介
刘惊惊,唯品会业务架构部高级架构师。主要负责用户线,营销线的业务架构,也参与库存系统的重构改造。
唯品会是一家立足于“全球精选,正品特卖”的电商网站,拥有4亿注册会员,日活约2千万会员。随着会员数量的增多,公司业务部门的飞速发展,和用户的沟通变得日益重要。沿用至今的消息网关,面对多变的业务和爆发式增长的消息面前,显得力不从心,多次大促出现性能瓶颈,急需重构来跟上公司业务发展的需要。
在本次重构中,将原来耦合在一起的消息发送渠道,被拆分成逻辑消息网关和物理发送渠道。逻辑消息网关(后面均用消息网关来代替)作为消息发送的总入口,对接上游各个业务系统,为业务系统提供友好的发送受理服务。逻辑消息网关的下游,对接各个物理投递渠道,负责对接第三方信道提供方(如电信,微信等)。
在重构前,消息网关和营销系统耦合较紧,包括定制化功能和共用的开发团队。在重构后,逐渐剥离了这种关系,使得消息网关定位为公司级的基础服务层。其主要职责是:受理投递、频次控制、尽力发送、反馈统计、削峰匹配慢速信道。
下面介绍一下消息网关在唯品会整体系统中的定位。如图1所示,消息网关定位于基础业务服务层,而物理信道定位在基础设施层,两者之间明确的区分了是否业务相关。
图1 消息网关在唯品会整体架构中的位置
那么消息网关具体内部应该是个什么样子,下面我们一起来看一下图2消息网关的内部构造。一个合格的消息网关应该具有以下的功能:业务友好的受理层,模板管理,频次控制,基于优先级队列的消息分发,反馈统计,延时发送,订阅控制,以及其他一些辅助功能。在第二部分将逐个模块的讲解。
图2 消息网关内部构造
在图2中,我们全面概览了消息网关内部应该具备的各个功能模块,下面我们逐个模块分解,看看各个部分的功能模块应该如何设计。
1. 消息的受理和分发
在实际的业务场景中,发送给会员,供应商和内部工作人员的消息,分为三种类型:关键性消息,通知类消息,以及营销类消息。关键性消息的特点是,低时延,低吞吐。通知类消息的特点是中时延,高吞吐。营销类消息的特点是中时延,高吞吐。针对不同的消息类型,应该选择不同的受理和分发模块,避免互相干扰。如图3所示。
图3 消息的受理和分发
对于关键性消息,某些渠道如短信,必须设置专用的独占信道来满足业务要求。如图4所示,中国移动信道质量较其他的供应商更好,所以对于关键性消息,通过专线连接主要服务商,并同时接入电信联通haproxy做备用线路。
图4 短信消息网关分发架构
2. 面向业务友好的接口
对使用消息发送服务的业务方来说,最好的接入方式是访问配置好模板的统一接口(对于需要高度个性化的营销系统,是个例外,营销系统倾向于在系统内部完成消息个性化的逻辑),尽量保持代码接口调用的固定,对于少量变动通过修改配置的方式完成。
这么做有两点好处,一是消息网关内部的优化和渠道变动逻辑,不需要被业务系统感知。二是使用预设模板降低了系统交互的开销。图5展示了统一接入接口在系统中的位置。
图5 面向业务友好的接口
3. 频次控制
消息网关需要保护会员不被过多的打扰,提供重要的兜底功能。随着业务的发展,商城、金融系统逐步独立,分散的营销系统往往无法从全局上兼顾整体营销。在营销强度模块正式运行之前,控制过度营销的最后一道闸门,控制在消息网关这里。
图6展示了消息网关在统一接入接口处检查频次控制的情况,对于超过限额的发送请求,会直接拒绝受理。
图6 频次控制
4. 用户订阅
现代消息发送系统都会提供用户退订功能,允许用户对希望接收的渠道和功能进行自主选择。那么用户订阅的关系应该维护在哪个系统比较合适呢?在实践中发现订阅关系维护在会员系统比较合适,消息网关查询订阅关系通过接口访问加缓存的方式去获取。同时各个渠道的退订消息,也需要经由消息网关上报到会员系统,更新订阅关系。图7展示了用户订阅的调用图。
图7 用户订阅
5. 失败重试
消息网关调用下游物理网关,不可避免的会出现调用失败的情况。那么这个时候就需要进行失败重试。失败重试分为2大类,重试失败需要落盘的,和重试失败直接记录异常日志的。验证码属于后者。对于通知类消息,可以容忍一定的时延,采用落盘定时任务轮询重试的方式比较合适。对于营销类消息,在时效期内可以落盘重试,极端情况也可以采用记录异常日志,然后直接丢弃的方式。
图8 失败重试
6. 反馈统计
从业务系统投递消息开始,会经过消息网关,物理消息渠道,第三方供应商等多个环节。每个环节都有可能造成消息的丢失,因此设计反馈统计机制很有必要,在错误排查和费用统计等方面有重要作用。实践中,采用反馈队列的方式进行异步统计,有分钟级的延时。对消息的最终发送结果,ETL到大数据,进行费用统计和费用分摊计算。
图9 反馈统计
业务模块开发采用唯品会自研的RPC调用框架Venus,采用OSP协议(唯品会私有协议)来封装RPC调用,底层通信协议采用Netty,传输协议使用Thrift。数据库技术选型MySQL。消息队列用Kafka,应对高吞吐量消息场景。ETL使用自研VDP(类似于阿里开源的Canal),解析binlog,同步数据到HDFS。图10演示了Venus框架的基本结构。
Venus框架是一个去中心化的RPC调用框架,Client端对Server端的调用由proxy进行转发。Client到LocalProxy,以及Local Proxy到Server端保持TCP长连接。
Proxy提供跨语言支持服务治理,服务发现,路由调度,限流熔断等功能,并额外支持HTTP GET +URL Path/Parameter,HTTP POST +JSON和RPC OSP协议的转换。Proxy分为LocalProxy和Remote Proxy。Local Proxy和Client部署在同一台服务器上,通过Domain Socket,减少TCP栈的调用开销。Remote Proxy作为备用链路,提供防火墙穿透,跨组织调用,非Java语言HTTP +JSON调用等功能。
图10 Venus框架基本结构
消息网关重度依赖kafka队列,分成消息受理队列,消息异步落盘队列,延时写入队列,反馈队列等。由于整个消息网关基本都是异步化操作,消息的分发有可能早于消息的落盘,这样在数据库消息发送状态更改时,就会出现无法找到的情况。可以采用延时队列,对消息发送状态的落盘动作进行延时写入。
1. 消息网关的监控
在日常运维层面,消息网关需要监控如下的指标:一是受理域,分发域各接口的调用情况。这部分数据已经在Venus框架内置,导入Mercury监控系统(类似于携程开源的CAT系统)。二是Kafka队列的消息堆积情况监控。三是采用Zabbix监控数据库服务器的CPU负载,内存使用量,磁盘IO等关键性指标。四是消息网关内部监控各物理渠道的投递时延。
图11 受理域请求监控
图12 物理渠道的投递时延监控
图13 Kafka消息堆积监控
2. 消息网关的降级措施
应用于生产系统的消息网关需要部署降级预案,保证故障发生时可以尽快的恢复,降低故障带来的负面影响。下面列出了部分重要的降级预案。
六、消息网关的几点思考
1、业务边界的划分
消息网关本身的设计并不复杂,使用的技术也是成熟的技术。消息网关的难点在于,对于系统业务边界的划分。由于和消息网关打交道的业务系统较多,很多功能可以由上游完成,也可以由下游实现,这个时候就需要权衡各个方面,找到折中的方案。在实践中,唯品会是这么划分业务系统边界的。
图14 消息网关业务边界的划分
2. 消息网关数据的使用
消息网关的投递的营销消息,订阅情况,打开率等是营销画像的重要组成部分。全景营销平台可以将消息投递情况、打开率、花费作为营销强度计算的数据源。
消息网关数据是各个业务部门费用分摊的主要依据,涉及上市公司的财务审计。
另外通过消息网关数据统计,可以作为各个渠道服务提供商选择和替换的依据。
3. 一些踩过的坑
短信物理网关的通讯协议按照运营商的不同,分成CMPP/SMGP/SGIP三种。都是基于TCP协议上的私有协议,需要自行处理断包和粘包问题。需要将包解析类CmppDecoder(移动)/SmgpDecoder(电信)/SgipDecoder(联通)配置到Mina框架NioSocketConnector的FilterChain里面。用IoSession来完成收发操作。
消息网关异步化引起的问题,比如消息数据异步落盘,可能落后于消息发送状态的更新。需要引进延时队列,通过定时重试解决此问题。
Redis使用上的最佳实践,比如Redis存储Value的String不要超过2k字节(理论最大值512M),以不超过1个网络包(MTU 1500byte)为佳。Set和Sorted的elements不要超过5000个。
压力测试机器上配置的最佳实践,比如需要配置/etc/sysctl.conf的选项:
net.ipv4.tcp_syncookies= 1
net.ipv4.tcp_tw_reuse =1
net.ipv4.tcp_tw_recycle= 1
net.ipv4.tcp_fin_timeout= 15
这篇文章总结了唯品会在消息网关重构中,使用的架构思路和经验。文中提到的消息网关,在今年6.16大促高峰期承担了亿级的消息发送压力,经过实践的检验,稳定可靠。希望对于行业内有相同应用场景的公司,能提供一定的设计借鉴作用。系统的设计,无法完美,希望不吝赐教,共同构建起更加高效和稳定的系统。