在使用RabbitMQ实现超时订单自动取消时,有两种解决方案,死信队列和延迟插件。
死信队列主要依赖的是死信队列和延迟队列的特新。通过设置消息存活时间,并且在指定时间内未能被消费,从而进入死信队列。消费者通过监听死信队列来处理订单的自动取消。这种方案的缺点就是可能存在消息阻塞的问题,影响后面消息消费。还有就是方案复杂,需要声明多个队列出来,增加系统的复杂度。
延迟插件使用就比较简单。基于rabbitmq_delayed_message_exchange插件,创建x-delayed-message类型的消息队列,当有延迟消息时,先会把消息存放在一个基于Erlang开发的Mnesia数据库中,然后通过定时器查询消息,并将消息投送到 x-delayed-message消息队列中。消费者监听x-delayed-message完成订单处理。
下面是基于延迟插件的实例
1. 生产端的实现
这里使用的是RabbitMQ Java客户端库com.rabbitmq:amqp-client
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class DelayedMessagePublisher {
private static final String EXCHANGE_NAME = "delayed_orders";
private static final String ROUTING_KEY = "order.timeout";
public void publishDelayedMessage(String message, int delayInSeconds) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // 设置RabbitMQ服务器地址
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// 声明一个延迟交换机,并设置类型为"x-delayed-message"
channel.exchangeDeclare(EXCHANGE_NAME, "x-delayed-message", true, false, null);
// 准备消息属性,设置延迟时间戳(单位为毫秒)
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.headers(new HashMap<String, Object>() {{
put("x-delay", delayInSeconds * 1000L); // 将延迟时间转换为毫秒
}})
.build();
// 发布带有延迟的消息
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, properties, message.getBytes());
System.out.println("Sent order cancellation message with delay of " + delayInSeconds + " seconds.");
}
}
public static void main(String[] args) throws IOException, TimeoutException {
DelayedMessagePublisher publisher = new DelayedMessagePublisher();
publisher.publishDelayedMessage("Order timeout for orderId XYZ", 60); // 发送一条将在60秒后投递的消息
}
}
在上述代码中,声明了一个延迟交换机,并设置了消息头x-delay来指定消息的延迟投递时间。消费端可以像普通队列那样绑定到这个延迟交换机,并定义自己的路由键规则。
2. 消费者的实现
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class DelayedMessageConsumer {
private static final String QUEUE_NAME = "order_timeout_queue";
private static final String EXCHANGE_NAME = "delayed_orders";
private static final String ROUTING_KEY = "order.timeout";
public void consumeDelayedMessages() throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost"); // 设置RabbitMQ服务器地址
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// 声明队列并绑定到延迟交换机
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);
// 定义消费者并启动消费循环
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println("Received delayed message: " + message);
// 在此处添加处理订单超时取消的逻辑
handleOrderCancellation(message);
};
CancelCallback cancelCallback = consumerTag -> System.out.println("Consumer cancelled");
channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
}
}
private void handleOrderCancellation(String message) {
// 根据接收到的消息内容(例如订单ID),调用相应的服务接口或方法进行订单取消操作
// 示例代码如下:
System.out.println("Cancelling order due to timeout: " + message);
// ... 实际操作,如更新数据库中的订单状态、释放库存、发送通知等
}
public static void main(String[] args) throws IOException, TimeoutException {
DelayedMessageConsumer consumer = new DelayedMessageConsumer();
consumer.consumeDelayedMessages();
}
}
在上述代码中,首先声明并绑定了一个队列到延迟交换机,当带有延迟的消息到达时,会自动投递到此队列。然后,创建了一个消费者,当有新消息投递到队列时,回调函数deliverCallback会被调用,从而执行订单超时取消的逻辑。实际应用中,handleOrderCancellation方法内应包含调用相关服务接口或方法来执行订单取消的具体操作。