面向特定客户端消息投递的多订阅者消息队列系统设计问询
我有一个系统,大量客户端与云端的自动扩缩容应用服务器集群建立持久化流式连接,每台服务器知晓连接到自身的所有客户端ID。当其他系统组件需要向特定在线客户端发送消息时,若消息发送时客户端尚未连接,需暂存消息直至其上线。我正寻求合适的路由/队列设计方案,以将消息投递至对应服务器,但现有消息/发布订阅/通知框架的设计假设似乎无法匹配该需求。
使用注册表方案
我可以搭建一个全局查找或注册表,存储服务器实例与所连接客户端ID列表的映射关系,由服务器在客户端连接/断开时维护该注册表,消息发送方负责查询注册表并向应用服务器发起连接。但该方案存在诸多需规避的问题:
- 强制采用推送语义,要求应用服务器开放端口监听消息
- 若客户端不在注册表中,需由消息发送方负责暂存消息
- 消息发送方需承担队列管理、重试、ACK处理等本该由消息队列框架提供的功能
- 负载均衡集群中的服务器不应被单独寻址,直接连接临时服务器属于设计缺陷
使用消息队列方案
可选方案包括AWS SQS、Google PubSub、RabbitMQ等,但该场景与这些框架的设计逻辑并不契合。核心设计选择包括:采用单全局队列(或发布订阅主题,消息携带目标客户端ID作为属性/元数据)、按服务器建队列、按客户端建队列。
按客户端建队列
该方案语义最清晰:客户端连接时,服务器订阅对应客户端的队列。优势在于队列中的消息日志即为该客户端的消息发送记录,便于追踪和调试客户端会话。但需支持由发送方或订阅方动态快速创建队列(预创建所有客户端队列不可行),而目前未找到具备该特性的框架。
按服务器建队列
可在新服务器启动时创建对应队列,发布者将消息发送至所有队列(扇出模式)。但服务器需在目标客户端未连接时确认消息(ACK),若客户端未上线,所有服务器都会确认消息,导致消息无法在客户端上线后投递。此外,服务器扩缩容时的队列垃圾回收操作也较为棘手。
单全局队列
该模式下设置一个全局客户端消息队列,所有应用服务器订阅,仅连接目标客户端的“正确”服务器处理并确认消息。但这不符合消息队列的设计逻辑:现有框架默认多订阅者等价,任意订阅者均可处理消息,无法保证消息投递至指定订阅者,且未处理消息会被视为异常,引入超时延迟等问题。
是否存在符合该需求的框架?该需求看似并不复杂。
补充说明
Stack Overflow上有一个相关问题,需求与我基本一致,但采纳的方案正是我所述的“按服务器建队列”,存在客户端未上线时消息被确认并丢弃的问题。
内容的提问来源于stack exchange,提问作者sidereal

