如何保證訊息不被重複消費
一. 重複訊息
為什麼會出現訊息重複?訊息重複的原因有兩個:1.生產時訊息重複,2.消費時訊息重複。
1.1 生產時訊息重複
由於生產者傳送訊息給MQ,在MQ確認的時候出現了網路波動,生產者沒有收到確認,實際上MQ已經接收到了訊息。這時候生產者就會重新傳送一遍這條訊息。
生產者中如果訊息未被確認,或確認失敗,我們可以使用定時任務+(redis/db)來進行訊息重試。
@Component
@Slf4J
public
class
SendMessage {
@
Autowired
private
MessageService
messageService;
@Autowired
private RabbitTemplate rabbitTemplate;
// 最大投遞次數
private static final int MAX_TRY_COUNT =
3;
/**
* 每30s拉取投遞失敗的訊息, 重新投遞
*/
@Scheduled(cron =
"0/30 * * * * ?")
public void resend() {
log.info(
"開始執行定時任務(重新投遞訊息)");
List<MsgLog> msgLogs = messageService.selectTimeoutMsg();
msgLogs.forEach(msgLog -> {
String msgId = msgLog.getMsgId();
if (msgLog.getTryCount() >= MAX_TRY_COUNT) {
messageService.updateStatus(msgId, Constant.MsgLogStatus.DELIVER_FAIL);
log.info(
"超過最大重試次數, 訊息投遞失敗, msgId: {}", msgId);
}
else {
messageService.updateTryCount(msgId, msgLog.getNextTryTime());
// 投遞次數+
1
CorrelationData correlationData = new CorrelationData(msgId);
rabbitTemplate.convertAndSend(msgLog.getExchange(), msgLog.getRoutingKey(), MessageHelper.objToMsg(msgLog.getMsg()), correlationData);
// 重新投遞
log.info(
"第 " + (msgLog.getTryCount() +
1) +
" 次重新投遞訊息");
}
});
log.info(
"定時任務執行結束(重新投遞訊息)");
}
}
1.2 消費時訊息重複
消費者消費成功後,再給MQ確認的時候出現了網路波動,MQ沒有接收到確認,為了保證訊息被消費,MQ就會繼續給消費者投遞之前的訊息。這時候消費者就接收到了兩條一樣的訊息。
修改消費者,模擬異常
@RabbitListener(queuesToDeclare =
@Queue(value =
"javatrip", durable =
"true"))
public
void receive(
String message,
@Headers Map<
String,
Object> headers, Channel channel) throws Exception{
System.out.println(
"重試"+System.currentTimeMillis());
System.out.println(message);
int i =
1 /
0;
}
配置yml重試策略
spring:
rabbitmq:
listener:
simple:
retry:
enabled: true # 開啟消費者進行重試
max-attempts:
5 # 最大重試次數
initial-interval:
3000 # 重試時間間隔
由於重複訊息是由於網路原因造成的,因此不可避免重複訊息。但是我們需要保證訊息的冪等性。
二. 如何保證訊息冪等性
讓每個訊息攜帶一個全域性的唯一ID,即可保證訊息的冪等性,具體消費過程為:
- 消費者獲取到訊息後先根據id去查詢redis/db是否存在該訊息
- 如果不存在,則正常消費,消費完畢後寫入redis/db
- 如果存在,則證明訊息被消費過,直接丟棄。
生產者
@PostMapping(
"/send")
public
void
sendMessage
(){
JSONObject jsonObject =
new JSONObject();
jsonObject.put(
"message",
"Java旅途");
String json = jsonObject.toJSONString();
Message message = MessageBuilder.withBody(json.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_JSON).setContentEncoding(
"UTF-8").setMessageId(UUID.randomUUID()+
"").build();
amqpTemplate.convertAndSend(
"javatrip",message);
}
消費者
@Component
@RabbitListener(queuesToDeclare =
@Queue(value =
"javatrip", durable =
"true"))
public
class Consumer {
@RabbitHandler
public
void receiveMessage(Message message) throws Exception {
Jedis jedis =
new Jedis(
"localhost",
6379);
String messageId = message.getMessageProperties().getMessageId();
String msg =
new
String(message.getBody(),
"UTF-8");
System.out.println(
"接收到的訊息為:"+msg+
"==訊息id為:"+messageId);
String messageIdRedis = jedis.get(
"messageId");
if(messageId == messageIdRedis){
return;
}
JSONObject jsonObject = JSONObject.parseObject(msg);
String email = jsonObject.getString(
"message");
jedis.set(
"messageId",messageId);
}
}
如果需要存入db的話,可以直接將這個ID設為訊息的主鍵,下次如果獲取到重複訊息進行消費時,由於資料庫主鍵的唯一性,則會直接丟擲異常。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69923331/viewspace-2710101/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 《RabbitMQ》如何保證訊息不被重複消費MQ
- 訊息佇列-如何保證訊息的不被重複消費(如何保證訊息消費的冪等性)佇列
- 阿里面試題剖析,如何保證訊息不被重複消費?阿里面試題
- 分散式訊息佇列:如何保證訊息不被重複消費?(訊息佇列消費的冪等性)分散式佇列
- 消費端如何保證訊息佇列MQ的有序消費佇列MQ
- 關於MQ的幾件小事(三)如何保證訊息不重複消費MQ
- Spring Cloud Stream如何處理訊息重複消費?SpringCloud
- MQ系列10:如何保證訊息冪等性消費MQ
- mq要如何處理訊息丟失、重複消費?MQ
- Kafka 如何保證訊息消費的全域性順序性Kafka
- 訊息中介軟體—RocketMQ訊息消費(三)(訊息消費重試)MQ
- 使用訊息中介軟體時,如何保證訊息僅僅被消費一次?
- Storm確保訊息被消費ORM
- RabbitMQ使用教程(五)如何保證佇列裡的訊息99.99%被消費?MQ佇列
- 解決RabbitMQ訊息丟失與重複消費問題MQ
- kafka 如何保證不重複消費又不丟失資料?Kafka
- 如何透過 SpringBoot+RabbitMQ 保證訊息100%投遞成功並被消費?(附原始碼)Spring BootMQ原始碼
- 實際業務處理 Kafka 訊息丟失、重複消費和順序消費的問題Kafka
- MQ 如何防止訊息重複入隊MQ
- 訊息佇列之如何保證訊息的可靠傳輸佇列
- kafka消費者消費訊息的流程Kafka
- RabbitMQ如何保證訊息的可達性MQ
- 《RabbitMQ》如何保證訊息的可靠性MQ
- RabbitMQ-如何保證訊息不丟失MQ
- 分散式訊息佇列:如何保證訊息的順序性分散式佇列
- Kafka如何保證訊息不丟之無訊息丟失配置Kafka
- RocketMq如何順序消費的訊息offestMQ
- Pulsar 也會重複消費?
- 如何保證訊息佇列的順序性?佇列
- 二、如何保證訊息佇列的高可用?佇列
- RocketMQ -- 訊息消費過程MQ
- RabbitMQ高階之如何保證訊息可靠性?MQ
- Spring Cloud Stream如何消費自己生產的訊息?SpringCloud
- hyperf 如何對AMQP訊息進行手動消費?MQ
- 阿里二面:要保證訊息不丟失,又不重複,訊息佇列怎麼選型?阿里佇列
- 訊息佇列——數十萬級訊息的消費方案佇列
- 如何保證訊息佇列的可靠性傳輸?佇列
- RabbitMQ多消費者順序性消費訊息實現MQ