RabbitMQ| 解決消息冪等性問題

消費者出現業務邏輯問題,自動補償機制

場景演示:

/**
 * @author 孫一鳴 on 2020/3/15
 */
@Service
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "sym"),
        exchange = @Exchange(value = "exchange.direct",type = "direct"),
        key = "sym"
))
public class rabbitService {
    @RabbitListener(queues = "sym")
    public void recive (Map map){
        System.out.println(map.get("data"));
        int i=1/0;
    }
}

@RabbitListener 底層 使用Aop進行攔截,如果程序沒有拋出異常,自動提交事務
// 如果Aop使用異常通知攔截 獲取異常信息的話,自動實現補償機制 ,該消息會緩存到rabbitmq服務器端進行存放,一直重試到不拋異常爲準。

補償(重試機制) 隊列服務器 發送補償請求

修改重試機制策略 一般默認情況下 間隔3秒重試一次
在這裏插入圖片描述
發送消息者發送消息,消費者消費消息時出現異常,此時隊列重試3次
出現3次日誌顯示

在這裏插入圖片描述

調用第三方接口,如果出現異常,調用重試機制

	@RabbitListener(queues = "fanout_email_queue")
	public void process(Message message, @Headers Map<String, Object> headers, Channel channel) throws Exception {
		String messageId = message.getMessageProperties().getMessageId();
		String msg = new String(message.getBody(), "UTF-8");
		System.out.println("郵件消費者獲取生產者消息msg:" + msg + ",消息id:" + messageId);
		// 重試機制都是間隔性

		JSONObject jsonObject = JSONObject.parseObject(msg);
		String email = jsonObject.getString("email");
		String emailUrl = "http://127.0.0.1:8083/sendEmail?email=" + email;
		System.out.println("郵件消費者開始調用第三方郵件服務器,emailUrl:" + emailUrl);
		JSONObject result = HttpClientUtils.httpGet(emailUrl);
		// 如果調用第三方郵件接口無法訪問,如何實現自動重試.
		if (result == null) {
			throw new Exception("調用第三方郵件服務器接口失敗!");
		}
		System.out.println("郵件消費者結束調用第三方郵件服務器成功,result:" + result + "程序執行結束");
		// 手動ack
		Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
		// 手動簽收
		channel.basicAck(deliveryTag, false);

	}
	// 默認是自動應答模式

在這裏插入圖片描述

MQ重試機制需要注意的問題

如何合適選擇重試機制:

  • 情況1: 消費者獲取到消息後,調用第三方接口,但接口暫時無法訪問,是否需要重試? (需要重試機制
  • 情況2: 消費者獲取到消息後,拋出數據轉換異常,是否需要重試?(不需要重試機制)需要發佈進行解決

消費者如果保證消息冪等性,不被重複消費
產生原因:網絡延遲傳輸中,消費出現異常或者是消費延遲消費,會造成MQ進行重試補償,在重試過程中,可能會造成重複消費。

解決辦法:
①使用全局MessageID判斷消費方使用同一個,解決冪等性。


@Component
public class FanoutProducer {
	@Autowired
	private AmqpTemplate amqpTemplate;

	public void send(String queueName) {
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("email", "644064779");
		jsonObject.put("timestamp", System.currentTimeMillis());
		String jsonString = jsonObject.toJSONString();
		System.out.println("jsonString:" + jsonString);
		// 生產者發送消息的時候需要設置消息id

		Message message = MessageBuilder.withBody(jsonString.getBytes())
				.setContentType(MessageProperties.CONTENT_TYPE_JSON).setContentEncoding("utf-8")
				.setMessageId(UUID.randomUUID() + "").build();

		amqpTemplate.convertAndSend(queueName, message);
	}
}

②或者使用業務邏輯保證唯一(比如訂單號碼)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章