rabbitmq的消息的有顺序性
一、rabbitmq:拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点;或者就一个queue但是对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理;

RabbitMQ 实现消息顺序消费的完整方案
RabbitMQ 本身是一个基于消息队列的异步消息中间件,要保证严格的顺序消费需要特殊设计和配置。以下是实现消息顺序消费的几种有效方案:
一、单队列单消费者模式(最简单方案)
实现原理
-
一个队列只对应一个消费者
-
天然保证先进先出(FIFO)顺序
实现代码
// 生产者
rabbitTemplate.convertAndSend("order.queue", "消息1");
rabbitTemplate.convertAndSend("order.queue", "消息2");
// 消费者
@RabbitListener(queues = "order.queue")
public void processOrder(String message) {
// 按顺序处理消息
}
优缺点:
-
✅ 优点:实现简单,严格保证顺序
-
❌ 缺点:无法水平扩展消费者,性能受限
二、通过路由键来实现消息分组有顺序性(推荐方案)
实现原理
-
按业务ID将消息分组(如同一订单号)
-
相同分组的消息路由到同一队列
-
每个队列只有一个消费者
代码示例1
// 配置交换机和队列
@Bean
public DirectExchange orderExchange() {
return new DirectExchange("order.exchange");
}
@Bean
public Queue orderQueue1() { return new Queue("order.queue.1"); }
@Bean
public Queue orderQueue2() { return new Queue("order.queue.2"); }
// 生产者发送消息(按订单ID路由)
public void sendOrderMessage(Order order) {
String routingKey = "order.queue." + (order.getOrderId().hashCode() % 2 + 1);
rabbitTemplate.convertAndSend("order.exchange", routingKey, order);
}
// 消费者
@RabbitListener(queues = "order.queue.1")
public void processOrder1(Order order) {
// 处理订单
}
@RabbitListener(queues = "order.queue.2")
public void processOrder2(Order order) {
// 处理订单
}
优缺点:
-
✅ 优点:可扩展消费者数量,相同业务ID消息顺序保证
-
❌ 缺点:需要设计合理的分组策略
代码示例2
电商订单状态变更顺序消费案例
1、场景说明
-
订单状态变更必须严格按顺序处理:创建 → 支付 → 发货 → 完成
-
同一订单的不同状态消息必须顺序消费
-
不同订单之间可以并行处理
2、实现方案设计
采用消息分组方案,根据订单ID将消息路由到不同队列,每个队列一个消费者保证顺序
3、完整实现代码
配置类
@Configuration
public class OrderRabbitConfig {
// 订单交换机
@Bean
public DirectExchange orderExchange() {
return new DirectExchange("order.exchange", true, false);
}
// 订单队列(根据订单ID哈希路由到3个队列)
@Bean
public Queue orderQueue0() {
return new Queue("order.queue.0", true);
}
@Bean
public Queue orderQueue1() {
return new Queue("order.queue.1", true);
}
@Bean
public Queue orderQueue2() {
return new Queue("order.queue.2", true);
}
// 交换机与队列进行绑定关系
@Bean
public Binding orderBinding0() {
return BindingBuilder.bind(orderQueue0())
.to(orderExchange())
.with("order.0");
}
@Bean
public Binding orderBinding1() {
return BindingBuilder.bind(orderQueue1())
.to(orderExchange())
.with("order.1");
}
@Bean
public Binding orderBinding2() {
return BindingBuilder.bind(orderQueue2())
.to(orderExchange())
.with("order.2");
}
}
订单状态消息
@Data
public class OrderStatusMessage {
private String orderId; // 订单ID
private String status; // 订单状态
private Long timestamp; // 消息时间戳
private Integer sequence; // 消息序列号
// 状态枚举
public static final String CREATED = "CREATED";
public static final String PAID = "PAID";
public static final String SHIPPED = "SHIPPED";
public static final String COMPLETED = "COMPLETED";
}
生产者服务
@Service
public class OrderProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendOrderStatus(OrderStatusMessage message) {
// 根据订单ID决定路由键(相同订单始终到同一队列)
String routingKey = "order." + Math.abs(message.getOrderId().hashCode() % 3);
rabbitTemplate.convertAndSend(
"order.exchange",
routingKey,
message,
msg -> {
// 设置消息持久化
msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
return msg;
});
log.info("发送订单状态消息: {} [{}]", message.getOrderId(), message.getStatus());
}
}
消费者服务
@Service
public class OrderConsumer {
// 记录每个订单的最后处理状态
private final Map<String, String> lastStatusMap = new ConcurrentHashMap<>();
@RabbitListener(queues = "order.queue.0")
public void processQueue0(OrderStatusMessage message, Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
processOrderMessage(message, channel, tag);
}
@RabbitListener(queues = "order.queue.1")
public void processQueue1(OrderStatusMessage message, Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
processOrderMessage(message, channel, tag);
}
@RabbitListener(queues = "order.queue.2")
public void processQueue2(OrderStatusMessage message, Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
processOrderMessage(message, channel, tag);
}
private void processOrderMessage(OrderStatusMessage message, Channel channel, long tag)
throws IOException {
try {
// 检查消息顺序
String lastStatus = lastStatusMap.get(message.getOrderId());
if (!checkStatusSequence(lastStatus, message.getStatus())) {
log.warn("订单状态顺序错误! 订单ID: {}, 当前状态: {}, 收到状态: {}",
message.getOrderId(), lastStatus, message.getStatus());
channel.basicNack(tag, false, false); // 丢弃错误顺序消息
return;
}
// 模拟业务处理
processOrderStatus(message);
// 更新最后状态
lastStatusMap.put(message.getOrderId(), message.getStatus());
// 确认消息
channel.basicAck(tag, false);
} catch (Exception e) {
log.error("处理订单消息异常", e);
channel.basicNack(tag, false, false); // 处理失败不再重试
}
}
private boolean checkStatusSequence(String lastStatus, String newStatus) {
if (lastStatus == null) {
return OrderStatusMessage.CREATED.equals(newStatus);
}
switch (lastStatus) {
case OrderStatusMessage.CREATED:
return OrderStatusMessage.PAID.equals(newStatus);
case OrderStatusMessage.PAID:
return OrderStatusMessage.SHIPPED.equals(newStatus);
case OrderStatusMessage.SHIPPED:
return OrderStatusMessage.COMPLETED.equals(newStatus);
default:
return false;
}
}
private void processOrderStatus(OrderStatusMessage message) {
// 模拟处理耗时
try {
Thread.sleep(new Random().nextInt(100));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log.info("处理订单状态: {} [{}]", message.getOrderId(), message.getStatus());
}
}
测试验证
@SpringBootTest
public class OrderMessageTest {
@Autowired
private OrderProducer orderProducer;
@Test
public void testOrderSequence() throws InterruptedException {
String orderId1 = "order-1001";
String orderId2 = "order-1002";
// 正确顺序的消息
sendOrderMessages(orderId1,
OrderStatusMessage.CREATED,
OrderStatusMessage.PAID,
OrderStatusMessage.SHIPPED,
OrderStatusMessage.COMPLETED);
// 乱序的消息(将被拒绝)
sendOrderMessages(orderId2,
OrderStatusMessage.PAID, // 错误:缺少CREATED
OrderStatusMessage.CREATED,
OrderStatusMessage.COMPLETED, // 错误:缺少SHIPPED
OrderStatusMessage.SHIPPED);
// 等待消费者处理
Thread.sleep(3000);
}
private void sendOrderMessages(String orderId, String... statuses) {
for (int i = 0; i < statuses.length; i++) {
OrderStatusMessage message = new OrderStatusMessage();
message.setOrderId(orderId);
message.setStatus(statuses[i]);
message.setTimestamp(System.currentTimeMillis());
message.setSequence(i + 1);
orderProducer.sendOrderStatus(message);
}
}
}
预期输出
发送订单状态消息: order-1001 [CREATED]
发送订单状态消息: order-1001 [PAID]
发送订单状态消息: order-1001 [SHIPPED]
发送订单状态消息: order-1001 [COMPLETED]
发送订单状态消息: order-1002 [PAID]
发送订单状态消息: order-1002 [CREATED]
发送订单状态消息: order-1002 [COMPLETED]
发送订单状态消息: order-1002 [SHIPPED]
处理订单状态: order-1001 [CREATED]
处理订单状态: order-1001 [PAID]
处理订单状态: order-1001 [SHIPPED]
处理订单状态: order-1001 [COMPLETED]
订单状态顺序错误! 订单ID: order-1002, 当前状态: null, 收到状态: PAID
处理订单状态: order-1002 [CREATED]
订单状态顺序错误! 订单ID: order-1002, 当前状态: CREATED, 收到状态: COMPLETED
处理订单状态: order-1002 [SHIPPED]

浙公网安备 33010602011771号