MQ如何防止消息被重复消费
1.消费者手动确认消息
在消费者消费消息后,通过调用basic.ack()方法手动确认消息已被消费。这样一来,RabbitMQ就会从队列中删除该消息,防止消息被重复消费。
// 创建连接和频道
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 设置消息确认模式为手动确认
channel.basicQos(1);
// 定义消息消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
// 模拟消费消息
String message = new String(body, "UTF-8");
System.out.println("Received message: " + message);
// 手动确认消息消费
channel.basicAck(envelope.getDeliveryTag(), false);
} catch (Exception e) {
// 消息消费失败,进行异常处理
channel.basicNack(envelope.getDeliveryTag(), false, true);
}
}
};
// 开始消费消息
channel.basicConsume(queueName, false, consumer);
2.消息去重(使用乐观锁)
在消费者消费消息前,可以将消息的唯一标识保存在数据库或缓存中。在消费者接收到消息后,先检查数据库或缓存中是否存在该消息的唯一标识,如果存在,则表示该消息已经被消费过,可以忽略;如果不存在,则表示该消息是新的,可以进行消费。
/ 创建连接和频道
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 定义消息消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
// 模拟消费消息
String message = new String(body, "UTF-8");
System.out.println("Received message: " + message);
// 判断消息是否已经消费过,可以通过数据库或缓存进行判断
if (!isMessageConsumed(message)) {
// 进行消息消费
consumeMessage(message);
}
// 手动确认消息消费
channel.basicAck(envelope.getDeliveryTag(), false);
} catch (Exception e) {
// 消息消费失败,进行异常处理
channel.basicNack(envelope.getDeliveryTag(), false, true);
}
}
};
// 开始消费消息
channel.basicConsume(queueName, false, consumer);
3.使用消息的全局唯一标识
可以在消息的属性中添加一个全局唯一标识,例如UUID,确保每条消息都具有唯一性。消费者在消费消息时,可以通过检查全局唯一标识来判断消息是否已经被消费过。
4.设置消息的过期时间
可以为消息设置一个过期时间,在消费者消费消息时,先判断消息是否已经过期,如果已经过期,则不进行消费。
java代码示例:
// 创建连接和频道
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 设置队列的消息过期时间
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-message-ttl", 5000); // 设置消息过期时间为5秒
channel.queueDeclare(queueName, true, false, false, arguments);
// 发布消息
String message = "Hello, RabbitMQ!";
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.expiration("5000") // 设置消息的过期时间为5秒
.build();
channel.basicPublish("", queueName, properties, message.getBytes("UTF-8"));
System.out.println("Sent message: " + message);
// 关闭连接和频道
channel.close();
connection.close();