本文首发自 InfoQ "告别传统金融消息架构:Apache Pulsar 在平安证券的实践".
在金融场景中,伴随着业务的扩展,应用系统也相应地增加更多的场景,这些新场景对消息系统提出更多样的需求,导致原有架构面临一系列挑战.在尝试使用 Apache Pulsar 后,平安证券决定在生产环境中进行实践.本文介绍了平安证券选择 Apache Pulsar 的原因,使用 Apache Pulsar 的场景,Apache Pulsar 实践应用中遇到的问题,以及使用 Apache Pulsar 的未来规划.背景介绍传统金融公司或券商一般会使用统一接入服务或组件来处理对外业务.接收到用户请求后,根据相应的业务规则将请求转到对应业务系统 / 模块.有些请求会转发给消息队列,请求写入后,下游业务系统从消息队列中获取请求,并在处理后通过消息队列原路返回给客户,整个请求过程封闭运行,功能有限.消息队列下传统架构带来的挑战平安证券采用的就是上述的传统架构,目前只支持消息队列.虽然我们有一定的开发能力,但也难以获取到该消息队列的细节信息.同时,由于是定制开发的系统,支持的语言比较有限.现有的消息队列对业务发展和业务创新等有以下不足:黑盒系统,难以观测:消息队列是一个黑盒系统,我们难以观测到架构的细节;直接交换(Direct Exchange),无法路由:由于架构目前只支持消息队列,无法支持需要路由的场景;弱校验接入,安全风险高:现有系统的密码认证,校验等检验较弱,安全风险较高;定制系统,有限语言支持:定制系统接入语言的支持有限,导致我们选择范围少,难以在原有系统基础上进行改革.
随着业务扩展和架构改进,公司现有的消息队列系统 / 组件面临着一系列挑战,而系统存在的许多问题如安全性等在金融场景中刻不容缓.金融场景的业务需求我们的业务需求主要分为三类:身份识别 & 安全控制,路由分发,审计.身份识别 & 安全控制身份识别主要用于确定接入消息队列的客户端和接入者的身份信息,指定相应的安全规则,拒绝不合法接入者,进而实现预期的安全要求.从最基础的层面看,需要识别控制接入的系统,IP,根据业务场景及特定需求,进行权限限制.路由分发路由分发指消息根据相应的规则由写入队列路由至对应的队列.现有的消息队列支持的场景有限,若想要支持更多场景,需要投入大量的时间和精力来进行开发(涉及上下游系统的改造),同时会引入其他问题.较好的解决方案是消息队列系统原生支持更多模式及特性,比如 TOPIC 模式,流式消息处理.如果消息队列系统可以支持路由,那么系统的接入复杂度就会大大降低,可以通过更优的方式对接入层进行操作,每个系统只需要对接一组 topic,路由负责分发;也可以更有针对性地优化性能(路由,转发,协议转化都是消耗性能的操作).
这种消息路由模式简化了系统的整体架构,目前我们的路由系统仍待优化:虽然路由分发的工作量得以减轻,但排查问题的步骤有所增加.比如在组件 A 发送消息后,组件 B 没有收到消息时,需要先检查组件 A 是否写入消息到 Topic A,路由模块是否成功路由这一消息,再看组件 B 是否正确订阅了这条消息.从目前的测试效果看,由于消息链路变长,时延增加.由于每个队列的消息都会持久化,导致存储和队列中都出现数据冗余.路由模块是新增模块,运维的学习成本较高.场景二:数据广播 -  - 降低时延数据广播是我们使用 Apache Pulsar 的另一个业务场景.数据广播采用发送 / 订阅模式,主要用于同步消息.很久之前,我们不需要同步行情到业务系统,或是通过其他方式(如同步数据库)实现.但随着业务的增长,同步时效和用户体验的竞争度越来越激烈.如何可以让用户更快地看到信息?以同步行情的场景为例,先同步数据库再查阅的方式,时延相对较长;而在广播模式中,业务系统只需订阅所有需要的 Topic,查阅时即可直接读取数据,有效降低时延.场景三:消息通知 -  - 安全管控我们使用到 Apache Pulsar 的第三个场景是消息通知.虽然消息通知涉及到的业务相对较少,但这一业务场景十分重要.整体业务流程图如下.由于信号源不唯一,因此在消息发布到计算引擎后,计算引擎需要根据信号源的信息进行逻辑,安全等方面的计算.计算完成后调起 Task,再由激活的 Task 向相关业务系统发送业务请求,执行后将结果返回给发起信号源的服务,该服务根据返回的结果触发下一个信号源.
目前常见的调用方式是客户端发起调用请求,服务端处理完成后返回响应即可.但引入总线以后(同步转异步),在多节点的部署场景中,节点 1 发出请求,服务端收到请求后返回处理结果,所有节点都需要监听这条处理结果,节点 2 收到归属节点 1 的响应消息时应该如何处理?节点 2 需要先订阅并获取回包的消息,判断是不是自身节点发起请求的响应,如果不是,则丢弃此消息.假如按照这种模式进行实现,则在发送消息时,每个节点都需要缓存自身发送的消息 ID;服务端处理完以后,按照协议回包数据需要带上请求的消息 ID,每个节点都订阅获取所有回包,并校验缓存中是否有该消息 ID,若不存在,则丢弃消息.
该实现方式下存在一个非常严峻的问题亟待解决:节点发起一个查询大量数据的请求时,假定 Apache Pulsar 设置一个消息 的大小为 8M,TPS 为 1000,那是不是每个节点都要收到这么多请求的回包流量呢?假如有 5 个节点,每个节点本应该只接收 200 个请求的回包流量就够了,但现在的模式需要每个节点承受 1000 个请求的回包流量,而其目的仅仅是为了过滤操作.如果节点负载性能达到上限,需要扩容节点,将导致网络带宽成倍增加.由于 Apache Pulsar 可以支持大量 Topic,虽然通过给每个节点配置一个回包队列等方式可以解决这一问题,但我们想尝试通过 broker 的 FILTER 功能,来解决该问题.2. 实现读写分离消息广播场景会涉及到读写分离.如果增加大量订阅节点,最好避免将所有节点的链接集中在 Topic 的 owner broker 上.针对这个问题,可行的解决方案是合理分配使用的 Topic 和 Partition.我们目前使用的 Apache Pulsar 2.7.2 还不支持读写分离,计划把 Apache Pulsar 升级到 2.8,就可以轻松实现读写分离,满足消息广播场景的需求.3. 解决多网卡问题基于公司网络安全考虑,内部存在多种网络分区及网段,不同的网络分区 / 网段使用不同的 IP,服务器存在多个网卡,供跨分区系统间通信.目前如果使用 IP 注册 broker,只能注册某个网段的 IP;如果使用域名注册 broker,则不同网络区域的 DNS 解析又需要进行不同的配置.如果 broker 可以支持多网卡通信,这些问题就不存在了.目前我们的解决方案是用 proxy 代理客户端的请求,外部系统也只连接到 proxy,我们也会为 proxy 增加一些高可用的配置.未来规划目前 ,我们在单机房,单集群线上小规模运行 Apache Pulsar,在上线初期未考虑建设双活.作为业务系统的基础设施,Apache Pulsar 自身可用性极为重要.因此,我们计划基于同城双中心单集群建设进行双活规划,如图如示:
在测试和使用 Apache Pulsar 的过程中,我们遇到了一些问题,感谢 Apache Pulsar 社区的积极响应.我们期待更多地参与到 Apache Pulsar 的研发中,也期待为 Apache Pulsar 和 Apache Pulsar 社区做出贡献.作者简介:王东松,平安证券经纪业务事业部研发工程师.陈翔,平安证券经纪事业部架构师.加入 Apache Pulsar 中文交流群