RabbitMQ发布订阅模式同一消费者多个实例如何防止重复消费?

微服务架构模式中,服务间的通信一般采用HTTP、RPC或者MQ(消息队列)。在这三种方案中,HTTP和RPC是一对一的方式,通常用来进行查询或者命令式的操作,MQ则多用于事件的发布和处理。

在实际项目中我们通常会遇到一种情况:

事件有多个订阅者,有的订阅者部署多个实例,要求每个事件只需要发布一次,每个订阅者都要能收到且仅能有其中一个实例收到并进行处理。

简单说就是既要所有订阅者都能收到消息,又要保证每个订阅者只能消费一次,不能重复消费。那么在使用RabbitMQ作为消息中间件时应该如何处理这个问题?

需求

微服务架构下使用RabbitMQ作为服务总线中间件,有这样一种场景,订单服务在用户提交订单后会发送OrderCreatedEvent事件,需要接收此事件的服务有以下几个:

  1. 日志服务(单点部署);
  2. 消息系统,给用户发送短信、邮件(单点部署);
  3. 仓储系统,接收通知备货,扣减库存等(多实例,有负载均衡),同一条消息仅允许一个实例消费;
  4. 财务系统,记录收入流水(多实例,有负载均衡),同一条消息仅允许一个实例消费;
  5. BI系统,做销售统计等;

问题

OrderCreatedEvent发出后,5个服务均可以接收到消息并进行处理,但是仓储和财务部署了多个实例,也就意味着每个实例都能收到相同的消息,这种情况下如何避免重复消费?

方案

  1. OrderCreatedEvent事件建立一个fanout exchange
  2. 每个需要处理OrderCreatedEvent的订阅者单独建立一个queue,订阅者的所有实例都消费这一个queue的消息。
  3. 将所有OrderCreatedEvent订阅者的queue都bind到第一步建立的exchange上。
  4. 事件发布者将OrderCreatedEvent消息发布到exchange,由exchange将消息路由到对应的queue,最后由queue锁定一个消费者实例并进行投递。

以下是示意图

四种主要 Exchange 类型

RabbitMQ主要有四种核心的Exchange类型:Fanout(广播)、Direct(精确匹配)、Topic(模式匹配)和Headers(按头匹配),它们决定了消息如何从交换机路由到队列,其中Direct、Topic、Fanout最常用,而Headers和一些插件类型(如x-delayed-message)提供更灵活的路由功能。

  1. Fanout (扇出)

    • 特点:将消息广播到所有绑定到它的队列。
    • 路由规则:忽略路由键 (Routing Key)。
    • 场景:适用于日志系统、通知广播等需要消息分发到多个消费者的场景。
  2. Direct (直连)

    • 特点:消息根据与队列绑定的精确路由键来路由。
    • 路由规则消息的 Routing Key 必须完全 匹配 队列绑定的 Routing Key
    • 场景:一个消费者处理特定类型的任务,如处理"用户注册"消息。
  3. Topic (主题)

    • 特点 :使用通配符(* 匹配一个词,# 匹配零个或多个词)进行模式匹配路由。
    • 路由规则消息的 Routing Key 模式需要与 队列绑定的模式 匹配。
    • 场景 :日志级别过滤,如logs.info.* 匹配所有 info 级别的日志,logs.# 匹配所有日志.
  4. Headers (头部)

    • 特点:根据消息的 Headers 属性(键值对)进行路由,而不是 Routing Key。
    • 路由规则:匹配消息 Headers 中的键值对与绑定时设置的键值对。
    • 场景:当需要根据复杂属性匹配时,较少使用,因为性能不如前三者。

Demo

后面我将会提供以上方案的具体实现,包含Java.net两种版本。

相关推荐
止语Lab2 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活2 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困2 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn2 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530142 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908402 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金2 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒3 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境3 小时前
提升 Python 代码健壮性的方法大盘点
后端·python