2021年 第032篇
Pike 2.0致力于为美团提供一套易接入、高可靠、高性能的双向消息投递服务。本文首先从系统架构升级、工作模式升级、长稳保活机制升级等方面介绍了Pike 2.0的技术演进,然后介绍了Pike 2.0在直播、游戏等新业务场景下的特性支持。希望本文能给对消息投递服务感兴趣或者从事相关工作的读者一些帮助和启发。
2015年,美团诞生了Shark终端网络通道,为公司移动端提供长连代理加速服务。Shark通过网络接入点的全球多地部署和保持长连来提升网络请求的端到端成功率,降低端到端延时,从而提升用户体验。
Pike 1.0是基于Shark长连通道实现的应用内推送服务。由于底层传输基于Shark长连通道,使得Pike 1.0天生便具有了低延时、高可靠、防DNS劫持等优秀基因。目前Pike 1.0在美团内部的实时互动、营销推送、状态下发、配置同步等业务场景都有广泛的应用。
移动端SDK会在每次长连接创建成功后,使用APPID、设备唯一标识UnionID(美团唯一标识、点评唯一标识等)向服务器发起注册,在注册成功之后业务服务端就可以通过Pike 1.0服务端SDK提供的接口,主动向设备的App推送消息。服务端推送的消息通过长连接通道抵达客户端,最后通过注册的回调接口投递给业务方。整体工作流程参见下图:
图1 Pike 1.0工作流程图
Pike 1.0底层传输基于Shark长连通道,所以Pike 1.0在以下几个方面有不错的表现:
Pike 1.0作为Shark的衍生产品固然有其闪光的地方,但是对Shark的强依赖所带来的痛点更是让开发人员叫苦不迭,主要痛点如下。
在客户端SDK方面,Pike 1.0代码与Shark代码结构耦合,共用底层通道建连、数据加解密、二进制协议等逻辑。如图展示了Pike 1.0与Shark在代码结构上的关系。
图2 Pike 1.0与Shark代码结构示意图
耦合带来的弊端一:代码优化升级困难。针对一个SDK的变更经常需要更多地考虑对另一个SDK是否有负面影响,是否影响面可控,这就无端地增加了开发成本。
耦合带来的弊端二:Shark与Pike 1.0的网络配置环境共用,如图所示,通过DebugPanel对SharkTunnel进行网络环境配置都会同时对Shark和Pike 1.0生效,但是业务方在使用的时候往往只关注其中的一个SDK,不同SDK之间的相互影响引入了很多客服问题,也给客服问题的排查带来了较多干扰因素。
Pike 1.0在同一个App上只支持一种设备唯一标识UnionID,不同App上注册使用的UnionID会有不同,例如美团使用美团唯一标识,点评则使用点评唯一标识。
假如一个业务只在一个App上使用的话Pike 1.0自然可以很好地工作,但是同一个业务有可能需要在多个App上同时使用(如图所示),如果业务方不对账号体系进行兼容的话,美团App上使用点评唯一标识作为推送标识的业务将无法工作,点评App上使用美团唯一标识作为推送标识的的业务也会无法工作。这就导致同一个业务在不同App上的推送标识ID逻辑会非常复杂,后端要同时维护多套账号体系之间的映射,才能解决账号体系混乱的问题。
图3 Pike 1.0账号体系不兼容示意图
Pike 1.0由于共用Shark的通道逻辑而缺乏推送场景专项优化,在检测通道异常、断连恢复等方面表现不够优秀。在通道可用性上,Shark与Pike 1.0关注的SLA也有着很大的不同。
例如,Shark在长连接通道不可用的情况下,可以通过降级短连接来规避业务网络请求持续失败所带来的成功率下降问题。但是对于Pike 1.0此时如果通道不能快速恢复的话就会造成业务消息投送失败,将直接影响消息投递成功率。所以Shark通道针对连接保活的公共逻辑并不能完美地应用在Pike 1.0业务场景上。
虽然Pike 1.0在Shark通道的基础上,进一步在协议层强化了心跳探测机制以提高通道可用性,但通道不能及时检测异常还是时有发生。此外,Pike 1.0内部使用的事件分发技术的可靠性还暂时没能达到100%,零星地会上报一些异常断连而导致推送不成功的客服问题。综上,针对推送连接不稳定专项优化的诉求也就不断被提上日程。
Pike 1.0现有的痛点在业务场景日益丰富的现状下遭遇了诸多挑战。力求解决Pike 1.0现有在Android和iOS平台运营上遇到的问题,一方面我们重新梳理产品架构与代码实现,另一方面我们决定与基础技术部另一个服务于H5的消息投递服务Pike Web进行产品融合,进而推出全新的升级产品——Pike 2.0。
下图展示了Pike 2.0的产品全景,针对Pike 1.0的现状,Pike 2.0前后端都做了诸多优化,包括技术架构升级、集群独立、协议扩展等。其中在客户端方面Pike 2.0提供了基于多语言实现服务于多平台的SDK,在服务端方面Pike使用部署Java应用的分布式集群来提供服务。
图4 Pike 2.0产品全景图
本文主要从客户端视角,详细阐述Pike 2.0 客户端SDK的技术方案设计,从原理上说明Pike 2.0带来的技术优势。
针对上文提及的Pike 1.0代码结构耦合的痛点,Pike 2.0进行了全新的架构升级,在代码结构、环境配置、服务集群等方面上都与Shark保持产品隔离。
经过接近一年的技术积累与沉淀,从Shark提炼的TunnelKit长连内核组件和TNTunnel通用通道组件已经趋于稳定,所以Pike 2.0选择基于TunnelKit与TNTunnel来构建双向消息通道服务。具体优势有:
图5 客户端架构演进图
整体架构如图所示,包括Pike接口层、Pike通道层、TNTunnel通道层和TunnelKit长连内核层。
Pike接口层旨在为主流前端技术栈中所有需要应用内消息服务的业务提供简洁可靠的接口:
Pike通道层是特性的实现层,所有Pike接口层的API调用都会通过线程调度转变成封装的Task在Pike通道层完成具体的操作,Pike通道层是单线程模型,最大程度规避掉了线程安全问题。
Pike特性如下:
TNTunnel通道层是封装通用通道逻辑的功能层,主要涉及通道状态管理、协议封装、数据加解密等通用核心模块,是将通用通道逻辑从原先Shark通道中提炼而成的独立分层。Pike协议虽然是构建在现有Shark协议之上的应用层协议,但是Pike通道已经和原先的Shark通道在逻辑上完全解耦。
一方面,Pike 2.0会最大限度地复用Shark协议已成熟的技术,但是又不依赖于原有的Shark逻辑;另一方面,后续涉及二进制协议、安全协议等协议方面的升级优化都可以同时服务于Pike 2.0。
TunnelKit长连内核层主要功能是对接Socket来处理TCP或者UDP数据的发送与接收,管理各个连接的可用性等。每条Pike 2.0通道在TunnelKit中都是维护一条连接的,通过心跳保活机制和连接管理来保证在网络环境正常的情况下永远有一条连接来承载Pike数据。TunnelKit作为所有通道层的基础,是决定上层长连接通道稳定性最重要的一层。
在进行了全新架构升级的基础上,Pike针对上文提及的Pike 1.0账号体系混乱、推送连接不稳定的痛点重新设计并完善了工作机制。
其中,PikeClient作为Pike系统对接业务方的门户,在整个Pike 2.0系统中起着至关重要的作用,本文将以PikeClient为切入点介绍Pike的工作机制。
为了更好地维护Pike 2.0内部状态,PikeClient使用状态机来负责生命周期管理。
图6 PikeClient生命周期图
如图所示,PikeClient生命周期主要包括如下几个部分:
通过基于状态机的生命周期管理,既严格定义了PikeClient的工作流程,也可以准确监控其内部状态,提高了PikeClient的可维护性。
针对Pike 1.0混乱的账号体系痛点,Pike 2.0设计了全新的工作模式。如下图所示,Pike通过通道代理模块提供共享通道和独立通道两种模式来满足不通业务场景的需求。
图7 PikeClient工作模式示意图
共享通道模式是Pike 2.0基本的工作模式,新增的业务方在默认情况下都会使用该模式接入Pike 2.0。
在Pike 2.0中PikeClient与Pike通道服务是多对一的共享关系,每个业务方都会有自己的PikeClient,每个PikeClient都可以自定义消息推送标识ID而避免使用全局标识ID。业务后端可以精简推送标识逻辑,避免同时维护多套账号体系。
不同业务的PikeClient仅在接入层面做了业务隔离,在Pike 2.0通道中会由Pike通道服务完成统一的管理。这种多对一的共享关系使得所有Pike业务共享Pike 2.0通道特性,同时又可以针对每个业务的使用场景设置其特定的消息处理能力,每个接入Pike 2.0的业务方都只需要关注其自己的PikeClient即可。
独立通道模式是共享通道模式的拓展能力,Pike 2.0通过配置控制来决策是否切换至该模式。
Pike 2.0默认情况下所有业务方都是共享同一个Pike通道服务,然而鉴于业务场景的不同,每个业务对于消息吞吐量,消息时延等SLA指标的诉求也有差异,例如游戏业务对于消息时延过长的容忍性就比较差。针对特殊业务Pike 2.0提供了独立通道切换的能力支持。
所有PikeClient都通过Pike通道代理模块来对接Pike通道服务,Pike通道代理模块可以通过开关配置来控制PikeClient与特定的Pike通道服务协同工作。通过运用代理模式,既保证了原有结构的完整性,在不需要调整Pike通道代码逻辑的基础上就能够完成独立通道能力支持;又可以扩展通道切换能力,有效地管理通道切换的流程,让Pike 2.0通道最大化提供业务能力的同时避免资源浪费。
PikeClient的保活完全依赖Pike 2.0通道的保活,针对Pike 1.0推送连接不稳定的痛点,Pike 2.0通道在吸收Pike 1.0在保活机制方面沉淀的技术的基础上继续优化,最后设计出基于心跳探测、重连机制和通道巡检的三重保活机制。保活机制如下图:
图8 长连通道保活机制示意图
心跳探测是一种检查网络连接状态的常见手段,Pike长连接是TCP连接,TCP是虚拟连接,如果实际物理链路中出现诸如异常网络节点等因素导致连接出现异常,客户端和服务端并不能及时感应到连接异常,这时就会出现连接的状态处于ESTABLISHED状态,但连接可能已死的现象,心跳探测就是为了解决这种网络异常的技术方案。
客户端在心跳巡检计时器设置的心跳周期到达时判断是否存在上次心跳超时的异常,如果心跳超时则认为该连接已经不可用了,则会从连接池移除该连接并触发下文的重连机制。为了更快地发现通道异常,Pike 2.0对于心跳周期与心跳超时都是可配置的,针对不同App使用的场景可以灵活地设置;而且在每次发送上行数据的时候都会及时检测上次心跳是否超时,使得心跳探测结果不必等到下次心跳周期到达的时刻才知悉。
Pike 2.0并不是采用固定心跳频率来发送心跳包,Pike 2.0会利用通道的上下行数据包来动态减少心跳包的发送次数。此外,智能心跳也是Pike 2.0持续关注的话题。
重连机制是Pike 2.0作为长连接通道最核心的特性,也是Pike 2.0连接稳定性建设最重要的一环。
客户端会在发送消息、接收消息和心跳探测三个环节来决策是否需要触发重,一方面,如果主动发现连接池中可用连接不足则自动启动重连机制;另一方面,当现有可用连接关闭时也会自动触发重连机制。
Pike 2.0在重连的过程中会采用斐波那契数列退避算法来发起建连请求直至建连成功,一方面,Pike 2.0保证只要在网络可用的情况下总能够维持可用的长连接来服务于业务消息;另一方面,Pike 2.0在网络持续不可用的情况下避免连续建连使得系统满载。
通道巡检是在心跳探测和重连机制的基础上进一步提升Pike 2.0稳定性的有效机制。
客户端会根据心跳周期设置一个全局的巡检定时器,在每次定时器设置的时刻到达时,客户端会触发通道异常检测逻辑,一旦发现异常都会尝试重启通道。
Pike 2.0首先会在触发通道异常检测的时候获取当前通道状态,如果通道当前没有主动关闭但是通道处于不可用的状态,Pike 2.0会强制执行一次自启动;此外,在通道巡检的过程中,巡检管理器会不断收集消息收发过程中出现的超时异常,当超时异常次数连续累计超过配置的最大阈值时,Pike 2.0会认为当前通道可用性较低,需要强制关闭并执行一次自启动。
Pike 2.0作为Pike 1.0的升级产品,不只是为了解决Pike 1.0的痛点,通过新增特性以开拓新的应用场景也是Pike 2.0时刻关注的点。
随着公司内直播业务的兴起,公司内部也有很多业务方使用Pike 1.0作为弹幕、评论、直播间控制信令等下行实时消息的传输通道。但Pike 1.0基于早先的设计架构为弹幕、评论这种短时间需要处理海量消息的场景提供可靠服务的能力渐渐力不从心,主要表现在QPS大幅增长时,消息投递成功率降低、延时增加和系统性能开销增长等方面。Pike通过引入聚合消息为直播场景中消息的投递提出更加通用的解决方案。
直播场景中涉及的消息主要具备以下特点:
聚合消息在设计上主要采用下述思想:
Pike 2.0针对每个聚合单元都使用环形队列来维护消息列表,发送到该聚合单元的消息在经过优先级过滤之后都会插入队列Tail指针标示的位置,随着该聚合单元内消息不断增加最后达到最大队列长度时,Head指针会不断移动来给Tail指针腾出位置。聚合单元通过控制最大长度的环形队列来避免消息短时间井喷式增长带来的服务性能问题。
客户端在主动拉取的时候都会携带上一次获取到的消息处在环形队列中的偏移量,这样服务就会将偏移量标示的位置到Tail指针标示的位置之间的消息进行聚合作为本次拉取的结果一次性返回给客户端。不同客户端各自维护自己的偏移量,以此来避免服务端对于客户端的状态维护。
客户端与服务端的具体交互如图所示,客户端在加入聚合单元之后主动拉取,如果本次拉取携带的偏移量能够从服务的环形队列中获取到聚合消息,那么就将消息回调给业务之后马上进行下一次拉取操作。如果本次携带的偏移量已经位于环形队列Tail指针的位置,那么服务端将不做任何响应,客户端等待本次拉取超时之后开始下一次拉取操作,重复该流程直至客户端离开该聚合单元。与此同时,业务服务端如果有消息需要推送,则通过RPC的方式发送给Pike服务端,消息处理模块将执行消息分级策略过滤之后的有效消息插入环形队列。
图9 聚合消息交互流程图
Pike 1.0在设计之初就只适用于消息推送的场景,而Pike 2.0在其基础上演进为双向消息投递服务,即不仅支持下行的消息推送,还支持上行的消息投递。Pike 2.0在上行的消息投递方面进一步拓展了消息保序的功能,这里的消息保序主要包含两个层面的含义,首先每一个业务客户端发送的消息都最大程度地到达同一个业务服务器,其次这些消息是按照客户端发送的时序一致地到达该业务服务器。
为了使每一个业务客户端发送的消息都最大程度地到达同一个业务服务器,Pike 2.0引入了粘性会话的概念。粘性会话指的是同一客户端连接上的消息固定转发至某一特定的业务方机器处理,客户端断连重连后,保持新连接上的消息仍转发至该业务机器。
粘性会话可以归纳为如下的流程。首次业务登录的时候Pike 2.0服务器会利用负载均衡算法选择一台业务服务器,并将该业务服务器的路由标识通过业务登录结果通知客户端并保存,之后如果通道状态稳定的话所有的上行消息就都会投递到该业务服务器。如果期间通道状态波动出现断连的情况,Pike 2.0在发起重连之后会重新进行业务登录,这一次业务登录会将之前保存的路由标识重新上报给Pike 2.0服务器,这样Pike 2.0服务器就会通过路由标识重新绑定该业务服务器。
当然,如果路由标识指示的业务服务器已经停止提供服务,那么Pike 2.0服务器会重新通过负载均衡算法选择新的一台业务服务器,同时客户端会获取到新的路由标识,之后的逻辑重复该过程直至Pike 2.0客户端退出。
我们都知道TCP是有序的,那么在同一个TCP连接的前提下什么情况会出现客户端发送的消息乱序到达业务服务器呢?原因就是Pike 2.0服务器从TCP中读出消息之后将其投递给业务服务器是通过RPC异步调用的。
为了解决这种问题,最简单的方案当然是客户端将消息队列的发送窗口限定为1,每一条发送消息都在Pike 2.0服务器投递给业务服务器之后才能收到ACK,这时再发送下一条消息。但是考虑到网络传输在链路上的时延远远大于端上处理的时延,所以该方案的QPS被网络传输设了瓶颈,假设一个RTT是200ms,那么该方案理论也只能达到5的QPS。
Pike 2.0为了提高上行消息保序投递的QPS,采用服务端设置消息队列缓存的方案。如图所示,客户端可以在发送窗口允许的范围内一次性将多条消息发送出去,服务端把收到的消息都按顺序缓存在消息队列中,然后串行的通过RPC调用将这些缓存的消息依序投递给业务服务器。这种保序方案将QPS性能的瓶颈点从之前网络传输在链路上的时延转移到了RPC调用的时延上,而实际场景中一次RPC调用往往在几个毫秒之间,远远小于网络传输在链路上的时延,继而显著地提升了QPS。
图10 时序一致性示意图
Pike 2.0依赖美团监控平台Raptor完成监控体系建设,服务端和客户端都建设了各自完善的指标监控。Pike 2.0客户端通过利用Raptor的端到端指标能力和自定义指标能力输出了超过10+个监控指标来实时监控Pike系统,这些指标覆盖通道建立、消息投递、业务登录、系统异常等多维度。在实时指标监控的基础上Pike 2.0针对不同指标配置了报警阈值,以推送消息为例,如果特定App的大盘数据在每分钟的上下波动幅度超过10%,那么Raptor系统就会向Pike项目组成员推送告警信息。
基于所有Raptor监控指标,Pike 2.0提炼核心SLA指标如下:
指标名称 | 指标定义 | 指标意义 |
---|---|---|
上行消息投递成功率 | 上行消息从发送到收到ACK的成功率 | 代表Pike 2.0业务上行消息投递能力 |
上行消息投递延时 | 上行消息投递RTT | 同上 |
下行消息投递成功率 | 下行消息从发送到收到ACK的成功率 | 代表Pike 2.0业务下行消息投递能力 |
下行消息投递延时 | 下行消息投递RTT | 同上 |
通道可用耗时 | 通道从建立到可以传递消息的时间 | 代表Pike 2.0通道建连能力 |
日均消息量 | 日均投递的上下行消息总量 | 代表Pike 2.0产品服务能力 |
Pike 2.0会定期输出基于核心SLA指标的大盘数据报表,同时可以基于App、业务类型、网络类型等多维度对数据进行筛选以满足不同用户对于指标数据的需求。
监控体系能从全局的角度反映Pike 2.0系统稳定性,针对个案用户,Pike管理平台提供完整的链路追踪信息。
每个Pike 2.0连接都由唯一标识Token来区分,通过该唯一标识Token在Pike管理平台的“连接嗅探”模块主动探测便能获得对应连接上所有信令的交互流程。如图所示,流程中明确标注了客户端建立连接、发起鉴权、绑定别名等信令,点击对应信令可以跳转信令详情进一步查看该信令所携带的信息,再结合SDK埋点在美团日志服务Logan的离线日志就可以快速发现并定位问题。
图11 个案用户追踪信令交互图
截至2021年6月,Pike共接入业务200+个,日均消息总量约50亿+,Pike 2.0消息到达率>99.5%(相比Pike 1.0提升0.4% ),Pike 2.0平均端到端延时<220ms(相比Pike 1.0减少约37%)。
部分应用案例:
Pike在美团应用广泛,目前主要集中在实时触达、互动直播、移动同步等业务场景,随着公司业务的快速发展,Pike对可用性、易用性、可扩展性提出了更高要求,希望提升各种业务场景下的网络体验,因此Pike未来的规划重点主要是:提供多端、多场景下的网络通信方案,不断完善协议生态,在各种应用场景下对抗复杂网络。
健午、佳猛、陆凯、冯江等,均来自美团基础技术部-前端技术中心。