1. 引言
在微服务架构中,服务之间的同步调用会带来耦合度高、响应变慢、流量冲击等问题。消息队列(Message Queue)作为异步通信的核心组件,能够有效实现系统解耦、流量削峰和异步处理。本文将以 Spring Boot 为技术底座,系统讲解 RabbitMQ 与 Kafka 两大主流消息中间件的集成方式、可靠性保障、幂等性处理以及选型对比,帮助你在实际项目中做出合理的技术决策。
2. 消息队列的典型使用场景
2.1 异步处理
用户注册后需要发送欢迎邮件、短信通知、初始化用户画像等操作。如果这些操作都在注册接口内同步执行,接口响应时间会显著增加。通过消息队列,注册接口只需将"用户注册成功"事件发布到 MQ,消费者异步完成后续操作,接口响应时间大幅缩短。
2.2 流量削峰
秒杀、大促等场景下,瞬时请求量可能远超系统处理能力。将请求先写入消息队列,后端服务按照自身处理能力从队列中拉取消息,避免数据库被瞬间打垮。消息队列在这里起到了"缓冲池"的作用。
2.3 系统解耦
订单系统下单后,需要通知库存系统扣减库存、通知积分系统增加积分、通知物流系统创建运单。如果采用同步调用,每新增一个下游系统,订单系统都要修改代码。引入消息队列后,订单系统只需发布"订单创建"事件,下游系统各自订阅即可,系统间的依赖关系从"强耦合"变为"弱耦合"。
下面是消息队列在微服务架构中的整体定位示意图:
#mermaid-svg-8GYZCHvl8GEj5jg7{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8GYZCHvl8GEj5jg7 .error-icon{fill:#552222;}#mermaid-svg-8GYZCHvl8GEj5jg7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8GYZCHvl8GEj5jg7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .marker.cross{stroke:#333333;}#mermaid-svg-8GYZCHvl8GEj5jg7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8GYZCHvl8GEj5jg7 p{margin:0;}#mermaid-svg-8GYZCHvl8GEj5jg7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .cluster-label text{fill:#333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .cluster-label span{color:#333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .cluster-label span p{background-color:transparent;}#mermaid-svg-8GYZCHvl8GEj5jg7 .label text,#mermaid-svg-8GYZCHvl8GEj5jg7 span{fill:#333;color:#333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .node rect,#mermaid-svg-8GYZCHvl8GEj5jg7 .node circle,#mermaid-svg-8GYZCHvl8GEj5jg7 .node ellipse,#mermaid-svg-8GYZCHvl8GEj5jg7 .node polygon,#mermaid-svg-8GYZCHvl8GEj5jg7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8GYZCHvl8GEj5jg7 .rough-node .label text,#mermaid-svg-8GYZCHvl8GEj5jg7 .node .label text,#mermaid-svg-8GYZCHvl8GEj5jg7 .image-shape .label,#mermaid-svg-8GYZCHvl8GEj5jg7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-8GYZCHvl8GEj5jg7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8GYZCHvl8GEj5jg7 .rough-node .label,#mermaid-svg-8GYZCHvl8GEj5jg7 .node .label,#mermaid-svg-8GYZCHvl8GEj5jg7 .image-shape .label,#mermaid-svg-8GYZCHvl8GEj5jg7 .icon-shape .label{text-align:center;}#mermaid-svg-8GYZCHvl8GEj5jg7 .node.clickable{cursor:pointer;}#mermaid-svg-8GYZCHvl8GEj5jg7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .arrowheadPath{fill:#333333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8GYZCHvl8GEj5jg7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8GYZCHvl8GEj5jg7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8GYZCHvl8GEj5jg7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8GYZCHvl8GEj5jg7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8GYZCHvl8GEj5jg7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8GYZCHvl8GEj5jg7 .cluster text{fill:#333;}#mermaid-svg-8GYZCHvl8GEj5jg7 .cluster span{color:#333;}#mermaid-svg-8GYZCHvl8GEj5jg7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8GYZCHvl8GEj5jg7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8GYZCHvl8GEj5jg7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-8GYZCHvl8GEj5jg7 .icon-shape,#mermaid-svg-8GYZCHvl8GEj5jg7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8GYZCHvl8GEj5jg7 .icon-shape p,#mermaid-svg-8GYZCHvl8GEj5jg7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8GYZCHvl8GEj5jg7 .icon-shape .label rect,#mermaid-svg-8GYZCHvl8GEj5jg7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8GYZCHvl8GEj5jg7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8GYZCHvl8GEj5jg7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8GYZCHvl8GEj5jg7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
发布事件
异步消费
异步消费
异步消费
生产者服务
消息队列 MQ
消费者服务A
消费者服务B
消费者服务C
数据库
3. RabbitMQ 集成
3.1 引入依赖
在 pom.xml 中添加 spring-boot-starter-amqp:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3.2 配置文件
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
3.3 定义队列、交换机与绑定
@Configuration
public class RabbitConfig {
public static final String EXCHANGE = "order.exchange";
public static final String QUEUE = "order.queue";
public static final String ROUTING_KEY = "order.created";
@Bean
public TopicExchange orderExchange() {
return new TopicExchange(EXCHANGE);
}
@Bean
public Queue orderQueue() {
return new Queue(QUEUE, true);
}
@Bean
public Binding binding() {
return BindingBuilder.bind(orderQueue())
.to(orderExchange())
.with(ROUTING_KEY);
}
}
3.4 生产者发送消息
使用 RabbitTemplate 发送消息:
@Service
public class OrderProducer {
private final RabbitTemplate rabbitTemplate;
public OrderProducer(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void sendOrderCreated(OrderCreatedEvent event) {
rabbitTemplate.convertAndSend(
RabbitConfig.EXCHANGE,
RabbitConfig.ROUTING_KEY,
event
);
}
}
3.5 消费者接收消息
使用 @RabbitListener 监听队列:
@Component
public class OrderConsumer {
@RabbitListener(queues = RabbitConfig.QUEUE)
public void handleOrderCreated(OrderCreatedEvent event) {
// 处理订单创建后的业务逻辑
log.info("收到订单创建事件: {}", event.getOrderId());
}
}
下面是 RabbitMQ 的交换机、队列与绑定关系示意图:
#mermaid-svg-JFE0vgHpPUOR3KWl{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JFE0vgHpPUOR3KWl .error-icon{fill:#552222;}#mermaid-svg-JFE0vgHpPUOR3KWl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JFE0vgHpPUOR3KWl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JFE0vgHpPUOR3KWl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JFE0vgHpPUOR3KWl .marker.cross{stroke:#333333;}#mermaid-svg-JFE0vgHpPUOR3KWl svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JFE0vgHpPUOR3KWl p{margin:0;}#mermaid-svg-JFE0vgHpPUOR3KWl .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-JFE0vgHpPUOR3KWl .cluster-label text{fill:#333;}#mermaid-svg-JFE0vgHpPUOR3KWl .cluster-label span{color:#333;}#mermaid-svg-JFE0vgHpPUOR3KWl .cluster-label span p{background-color:transparent;}#mermaid-svg-JFE0vgHpPUOR3KWl .label text,#mermaid-svg-JFE0vgHpPUOR3KWl span{fill:#333;color:#333;}#mermaid-svg-JFE0vgHpPUOR3KWl .node rect,#mermaid-svg-JFE0vgHpPUOR3KWl .node circle,#mermaid-svg-JFE0vgHpPUOR3KWl .node ellipse,#mermaid-svg-JFE0vgHpPUOR3KWl .node polygon,#mermaid-svg-JFE0vgHpPUOR3KWl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JFE0vgHpPUOR3KWl .rough-node .label text,#mermaid-svg-JFE0vgHpPUOR3KWl .node .label text,#mermaid-svg-JFE0vgHpPUOR3KWl .image-shape .label,#mermaid-svg-JFE0vgHpPUOR3KWl .icon-shape .label{text-anchor:middle;}#mermaid-svg-JFE0vgHpPUOR3KWl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JFE0vgHpPUOR3KWl .rough-node .label,#mermaid-svg-JFE0vgHpPUOR3KWl .node .label,#mermaid-svg-JFE0vgHpPUOR3KWl .image-shape .label,#mermaid-svg-JFE0vgHpPUOR3KWl .icon-shape .label{text-align:center;}#mermaid-svg-JFE0vgHpPUOR3KWl .node.clickable{cursor:pointer;}#mermaid-svg-JFE0vgHpPUOR3KWl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JFE0vgHpPUOR3KWl .arrowheadPath{fill:#333333;}#mermaid-svg-JFE0vgHpPUOR3KWl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JFE0vgHpPUOR3KWl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JFE0vgHpPUOR3KWl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JFE0vgHpPUOR3KWl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JFE0vgHpPUOR3KWl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JFE0vgHpPUOR3KWl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JFE0vgHpPUOR3KWl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JFE0vgHpPUOR3KWl .cluster text{fill:#333;}#mermaid-svg-JFE0vgHpPUOR3KWl .cluster span{color:#333;}#mermaid-svg-JFE0vgHpPUOR3KWl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JFE0vgHpPUOR3KWl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JFE0vgHpPUOR3KWl rect.text{fill:none;stroke-width:0;}#mermaid-svg-JFE0vgHpPUOR3KWl .icon-shape,#mermaid-svg-JFE0vgHpPUOR3KWl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JFE0vgHpPUOR3KWl .icon-shape p,#mermaid-svg-JFE0vgHpPUOR3KWl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JFE0vgHpPUOR3KWl .icon-shape .label rect,#mermaid-svg-JFE0vgHpPUOR3KWl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JFE0vgHpPUOR3KWl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JFE0vgHpPUOR3KWl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JFE0vgHpPUOR3KWl :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
convertAndSend
routingKey: order.created
@RabbitListener
生产者
TopicExchangeorder.exchange
Queueorder.queue
消费者
4. Kafka 集成
4.1 引入依赖
在 pom.xml 中添加 spring-kafka:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
4.2 配置文件
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
consumer:
group-id: order–group
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
properties:
spring.json.trusted.packages: "*"
4.3 生产者发送消息
使用 KafkaTemplate 发送消息:
@Service
public class OrderKafkaProducer {
private final KafkaTemplate<String, Object> kafkaTemplate;
public OrderKafkaProducer(KafkaTemplate<String, Object> kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void sendOrderCreated(OrderCreatedEvent event) {
kafkaTemplate.send("order.created", event.getOrderId(), event);
}
}
4.4 消费者接收消息
使用 @KafkaListener 监听主题:
@Component
public class OrderKafkaConsumer {
@KafkaListener(topics = "order.created", groupId = "order-group")
public void handleOrderCreated(OrderCreatedEvent event) {
// 处理订单创建后的业务逻辑
log.info("收到订单创建事件: {}", event.getOrderId());
}
}
下面是 Kafka 的主题、分区与消费者组消费模型示意图:
#mermaid-svg-NWpmNMNvooYygfvk{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NWpmNMNvooYygfvk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NWpmNMNvooYygfvk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NWpmNMNvooYygfvk .error-icon{fill:#552222;}#mermaid-svg-NWpmNMNvooYygfvk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NWpmNMNvooYygfvk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NWpmNMNvooYygfvk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NWpmNMNvooYygfvk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NWpmNMNvooYygfvk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NWpmNMNvooYygfvk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NWpmNMNvooYygfvk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NWpmNMNvooYygfvk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NWpmNMNvooYygfvk .marker.cross{stroke:#333333;}#mermaid-svg-NWpmNMNvooYygfvk svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NWpmNMNvooYygfvk p{margin:0;}#mermaid-svg-NWpmNMNvooYygfvk .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-NWpmNMNvooYygfvk .cluster-label text{fill:#333;}#mermaid-svg-NWpmNMNvooYygfvk .cluster-label span{color:#333;}#mermaid-svg-NWpmNMNvooYygfvk .cluster-label span p{background-color:transparent;}#mermaid-svg-NWpmNMNvooYygfvk .label text,#mermaid-svg-NWpmNMNvooYygfvk span{fill:#333;color:#333;}#mermaid-svg-NWpmNMNvooYygfvk .node rect,#mermaid-svg-NWpmNMNvooYygfvk .node circle,#mermaid-svg-NWpmNMNvooYygfvk .node ellipse,#mermaid-svg-NWpmNMNvooYygfvk .node polygon,#mermaid-svg-NWpmNMNvooYygfvk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NWpmNMNvooYygfvk .rough-node .label text,#mermaid-svg-NWpmNMNvooYygfvk .node .label text,#mermaid-svg-NWpmNMNvooYygfvk .image-shape .label,#mermaid-svg-NWpmNMNvooYygfvk .icon-shape .label{text-anchor:middle;}#mermaid-svg-NWpmNMNvooYygfvk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NWpmNMNvooYygfvk .rough-node .label,#mermaid-svg-NWpmNMNvooYygfvk .node .label,#mermaid-svg-NWpmNMNvooYygfvk .image-shape .label,#mermaid-svg-NWpmNMNvooYygfvk .icon-shape .label{text-align:center;}#mermaid-svg-NWpmNMNvooYygfvk .node.clickable{cursor:pointer;}#mermaid-svg-NWpmNMNvooYygfvk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NWpmNMNvooYygfvk .arrowheadPath{fill:#333333;}#mermaid-svg-NWpmNMNvooYygfvk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NWpmNMNvooYygfvk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NWpmNMNvooYygfvk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NWpmNMNvooYygfvk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NWpmNMNvooYygfvk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NWpmNMNvooYygfvk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NWpmNMNvooYygfvk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NWpmNMNvooYygfvk .cluster text{fill:#333;}#mermaid-svg-NWpmNMNvooYygfvk .cluster span{color:#333;}#mermaid-svg-NWpmNMNvooYygfvk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NWpmNMNvooYygfvk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NWpmNMNvooYygfvk rect.text{fill:none;stroke-width:0;}#mermaid-svg-NWpmNMNvooYygfvk .icon-shape,#mermaid-svg-NWpmNMNvooYygfvk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NWpmNMNvooYygfvk .icon-shape p,#mermaid-svg-NWpmNMNvooYygfvk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NWpmNMNvooYygfvk .icon-shape .label rect,#mermaid-svg-NWpmNMNvooYygfvk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NWpmNMNvooYygfvk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NWpmNMNvooYygfvk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NWpmNMNvooYygfvk :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
send
分区0
分区1
分区2
生产者
Topicorder.created
消费者组order-group
5. 消息可靠性保障
5.1 生产者确认
RabbitMQ 生产者确认:开启 publisher-confirm-type: correlated,通过 CorrelationData 回调确认消息是否到达交换机。
spring:
rabbitmq:
publisher-confirm-type: correlated
publisher-returns: true
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData);
correlationData.getFuture().whenComplete((confirm, ex) -> {
if (confirm != null && confirm.isAck()) {
log.info("消息已确认到达交换机");
} else {
log.error("消息发送失败,需要重试或补偿");
}
});
Kafka 生产者确认:设置 acks=all,等待所有副本确认写入。
spring:
kafka:
producer:
acks: all
retries: 3
5.2 消费者手动 ACK
RabbitMQ 手动 ACK:设置 acknowledge-mode: manual,在业务处理成功后手动确认。
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual
@RabbitListener(queues = RabbitConfig.QUEUE)
public void handle(OrderCreatedEvent event, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
try {
// 业务处理
channel.basicAck(tag, false);
} catch (Exception e) {
channel.basicNack(tag, false, true); // 重回队列
}
}
Kafka 手动 ACK:设置 enable-auto-commit: false,使用 Acknowledgment 手动提交偏移量。
spring:
kafka:
consumer:
enable-auto-commit: false
@KafkaListener(topics = "order.created", groupId = "order-group")
public void handle(OrderCreatedEvent event, Acknowledgment ack) {
try {
// 业务处理
ack.acknowledge();
} catch (Exception e) {
// 不提交偏移量,消息会重新消费
}
}
5.3 死信队列
当消息消费失败且重试达到上限时,将消息转入死信队列,便于后续人工排查或延迟重试。
RabbitMQ 死信队列配置:
@Bean
public Queue orderDlq() {
return QueueBuilder.durable("order.dlq").build();
}
@Bean
public Queue orderQueue() {
return QueueBuilder.durable("order.queue")
.withArgument("x-dead-letter-exchange", "")
.withArgument("x-dead-letter-routing-key", "order.dlq")
.build();
}
Kafka 死信主题:在消费者中捕获异常后,将消息转发到 order.created.dlt 主题。
@KafkaListener(topics = "order.created", groupId = "order-group")
public void handle(OrderCreatedEvent event) {
try {
// 业务处理
} catch (Exception e) {
kafkaTemplate.send("order.created.dlt", event);
}
}
下面是消息可靠性保障的整体流程示意图:
#mermaid-svg-DzHVT8dXc7gvGeR8{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DzHVT8dXc7gvGeR8 .error-icon{fill:#552222;}#mermaid-svg-DzHVT8dXc7gvGeR8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DzHVT8dXc7gvGeR8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .marker.cross{stroke:#333333;}#mermaid-svg-DzHVT8dXc7gvGeR8 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DzHVT8dXc7gvGeR8 p{margin:0;}#mermaid-svg-DzHVT8dXc7gvGeR8 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .cluster-label text{fill:#333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .cluster-label span{color:#333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .cluster-label span p{background-color:transparent;}#mermaid-svg-DzHVT8dXc7gvGeR8 .label text,#mermaid-svg-DzHVT8dXc7gvGeR8 span{fill:#333;color:#333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .node rect,#mermaid-svg-DzHVT8dXc7gvGeR8 .node circle,#mermaid-svg-DzHVT8dXc7gvGeR8 .node ellipse,#mermaid-svg-DzHVT8dXc7gvGeR8 .node polygon,#mermaid-svg-DzHVT8dXc7gvGeR8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DzHVT8dXc7gvGeR8 .rough-node .label text,#mermaid-svg-DzHVT8dXc7gvGeR8 .node .label text,#mermaid-svg-DzHVT8dXc7gvGeR8 .image-shape .label,#mermaid-svg-DzHVT8dXc7gvGeR8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-DzHVT8dXc7gvGeR8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DzHVT8dXc7gvGeR8 .rough-node .label,#mermaid-svg-DzHVT8dXc7gvGeR8 .node .label,#mermaid-svg-DzHVT8dXc7gvGeR8 .image-shape .label,#mermaid-svg-DzHVT8dXc7gvGeR8 .icon-shape .label{text-align:center;}#mermaid-svg-DzHVT8dXc7gvGeR8 .node.clickable{cursor:pointer;}#mermaid-svg-DzHVT8dXc7gvGeR8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .arrowheadPath{fill:#333333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DzHVT8dXc7gvGeR8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DzHVT8dXc7gvGeR8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DzHVT8dXc7gvGeR8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DzHVT8dXc7gvGeR8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DzHVT8dXc7gvGeR8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DzHVT8dXc7gvGeR8 .cluster text{fill:#333;}#mermaid-svg-DzHVT8dXc7gvGeR8 .cluster span{color:#333;}#mermaid-svg-DzHVT8dXc7gvGeR8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DzHVT8dXc7gvGeR8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DzHVT8dXc7gvGeR8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-DzHVT8dXc7gvGeR8 .icon-shape,#mermaid-svg-DzHVT8dXc7gvGeR8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DzHVT8dXc7gvGeR8 .icon-shape p,#mermaid-svg-DzHVT8dXc7gvGeR8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DzHVT8dXc7gvGeR8 .icon-shape .label rect,#mermaid-svg-DzHVT8dXc7gvGeR8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DzHVT8dXc7gvGeR8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DzHVT8dXc7gvGeR8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DzHVT8dXc7gvGeR8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
失败
成功
成功
失败
否
是
生产者发送消息
生产者确认?
重试或补偿
消息进入队列
消费者消费
消费成功?
手动 ACK 确认
重试达上限?
转入死信队列
6. 消息幂等性处理
消息队列通常提供"至少一次"(At-Least-Once)投递语义,消费者可能收到重复消息,因此幂等性处理是必须的。
6.1 去重表方案
在数据库中建立消息去重表,以消息唯一 ID 作为主键:
CREATE TABLE msg_dedup (
msg_id VARCHAR(64) PRIMARY KEY,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
@Transactional
public void processWithDedup(String msgId, Runnable businessLogic) {
int inserted = jdbcTemplate.update(
"INSERT IGNORE INTO msg_dedup (msg_id) VALUES (?)", msgId);
if (inserted > 0) {
businessLogic.run(); // 首次消费,执行业务
}
// 重复消息直接跳过
}
6.2 Redis 标记方案
利用 Redis 的 SETNX 命令实现幂等标记:
public boolean tryProcess(String msgId) {
Boolean success = redisTemplate.opsForValue()
.setIfAbsent("msg:" + msgId, "1", Duration.ofHours(24));
return Boolean.TRUE.equals(success);
}
6.3 业务状态机方案
在业务表中维护状态字段,通过状态流转判断消息是否已处理:
public void processOrder(String orderId, String targetStatus) {
int updated = orderMapper.updateStatusIfMatch(
orderId, "PENDING", targetStatus);
if (updated > 0) {
// 状态从 PENDING 成功流转,说明首次处理
}
}
下面是消息幂等性处理的三种方案对比示意图:
#mermaid-svg-iGdLjUtCiM8h2G4o{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iGdLjUtCiM8h2G4o .error-icon{fill:#552222;}#mermaid-svg-iGdLjUtCiM8h2G4o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iGdLjUtCiM8h2G4o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iGdLjUtCiM8h2G4o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iGdLjUtCiM8h2G4o .marker.cross{stroke:#333333;}#mermaid-svg-iGdLjUtCiM8h2G4o svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iGdLjUtCiM8h2G4o p{margin:0;}#mermaid-svg-iGdLjUtCiM8h2G4o .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-iGdLjUtCiM8h2G4o .cluster-label text{fill:#333;}#mermaid-svg-iGdLjUtCiM8h2G4o .cluster-label span{color:#333;}#mermaid-svg-iGdLjUtCiM8h2G4o .cluster-label span p{background-color:transparent;}#mermaid-svg-iGdLjUtCiM8h2G4o .label text,#mermaid-svg-iGdLjUtCiM8h2G4o span{fill:#333;color:#333;}#mermaid-svg-iGdLjUtCiM8h2G4o .node rect,#mermaid-svg-iGdLjUtCiM8h2G4o .node circle,#mermaid-svg-iGdLjUtCiM8h2G4o .node ellipse,#mermaid-svg-iGdLjUtCiM8h2G4o .node polygon,#mermaid-svg-iGdLjUtCiM8h2G4o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iGdLjUtCiM8h2G4o .rough-node .label text,#mermaid-svg-iGdLjUtCiM8h2G4o .node .label text,#mermaid-svg-iGdLjUtCiM8h2G4o .image-shape .label,#mermaid-svg-iGdLjUtCiM8h2G4o .icon-shape .label{text-anchor:middle;}#mermaid-svg-iGdLjUtCiM8h2G4o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iGdLjUtCiM8h2G4o .rough-node .label,#mermaid-svg-iGdLjUtCiM8h2G4o .node .label,#mermaid-svg-iGdLjUtCiM8h2G4o .image-shape .label,#mermaid-svg-iGdLjUtCiM8h2G4o .icon-shape .label{text-align:center;}#mermaid-svg-iGdLjUtCiM8h2G4o .node.clickable{cursor:pointer;}#mermaid-svg-iGdLjUtCiM8h2G4o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iGdLjUtCiM8h2G4o .arrowheadPath{fill:#333333;}#mermaid-svg-iGdLjUtCiM8h2G4o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iGdLjUtCiM8h2G4o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iGdLjUtCiM8h2G4o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iGdLjUtCiM8h2G4o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iGdLjUtCiM8h2G4o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iGdLjUtCiM8h2G4o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iGdLjUtCiM8h2G4o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iGdLjUtCiM8h2G4o .cluster text{fill:#333;}#mermaid-svg-iGdLjUtCiM8h2G4o .cluster span{color:#333;}#mermaid-svg-iGdLjUtCiM8h2G4o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iGdLjUtCiM8h2G4o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iGdLjUtCiM8h2G4o rect.text{fill:none;stroke-width:0;}#mermaid-svg-iGdLjUtCiM8h2G4o .icon-shape,#mermaid-svg-iGdLjUtCiM8h2G4o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iGdLjUtCiM8h2G4o .icon-shape p,#mermaid-svg-iGdLjUtCiM8h2G4o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iGdLjUtCiM8h2G4o .icon-shape .label rect,#mermaid-svg-iGdLjUtCiM8h2G4o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iGdLjUtCiM8h2G4o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iGdLjUtCiM8h2G4o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iGdLjUtCiM8h2G4o :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
去重表方案
Redis 标记方案
业务状态机方案
首次插入成功
SETNX 返回 true
状态更新成功
重复消息
SETNX 返回 false
状态更新失败
收到消息
消息已处理?
INSERT IGNOREmsg_dedup
SETNXmsg:msgId
状态流转PENDING → 目标状态
执行业务逻辑
直接跳过
7. 两种 MQ 的选型对比
| 吞吐量 | 万级/秒 | 百万级/秒 |
| 延迟 | 微秒级 | 毫秒级 |
| 消息模型 | 交换机/队列/路由键 | 主题/分区/消费者组 |
| 消息顺序 | 单队列内有序 | 单分区内有序 |
| 消息回溯 | 不支持 | 支持按偏移量回溯 |
| 运维复杂度 | 中等 | 较高(依赖 ZooKeeper/KRaft) |
| 典型场景 | 业务解耦、任务分发 | 日志采集、大数据管道、事件流 |
选型建议:如果业务以解耦、异步任务为主,追求低延迟和灵活路由,选择 RabbitMQ;如果业务涉及海量日志、实时流处理、需要消息回溯,选择 Kafka。
8. Spring Cloud Stream 的抽象层价值
Spring Cloud Stream 在 RabbitMQ 和 Kafka 之上提供了一层统一的消息编程模型。开发者只需面向 @EnableBinding、@StreamListener(或函数式编程模型中的 Supplier/Function/Consumer)编写代码,通过配置切换不同的消息中间件,无需修改业务代码。
@Bean
public Consumer<OrderCreatedEvent> orderCreatedConsumer() {
return event -> {
// 业务处理,与具体 MQ 无关
log.info("收到订单创建事件: {}", event.getOrderId());
};
}
spring:
cloud:
stream:
bindings:
orderCreatedConsumer-in-0:
destination: order.created
group: order–group
binder: rabbit # 或切换为 kafka
核心价值:降低消息中间件切换成本、屏蔽底层 API 差异、提供统一的消费者组与分区消费语义,适合需要保持架构灵活性的中大型项目。
9. 总结
本文从异步处理、流量削峰、系统解耦三个典型场景出发,分别介绍了 RabbitMQ 与 Kafka 在 Spring Boot 中的集成方式,并深入探讨了消息可靠性保障(生产者确认、手动 ACK、死信队列)与幂等性处理(去重表、Redis 标记、业务状态机)两大核心难点。最后通过选型对比和 Spring Cloud Stream 抽象层的介绍,帮助你根据业务场景做出合理的技术决策。在实际项目中,可靠性保障与幂等性设计往往比 MQ 本身的选择更为关键,务必在架构设计阶段就纳入考量。



