首先講解防止生產者丟失信息處理。
1、設置發送消息確認: publisher-confirms: true
2、啓動消息失敗返回: publisher-returns: true
在yml中配置如下
spring:
rabbitmq: host: localhost port: 5672 virtual-host: mall username: guest password: guest publisher-confirms: true #如果對異步消息需要回調必須設置爲true publisher-returns: true listener: direct: acknowledge-mode: manual simple: retry: enabled: true max-attempts: 3 initial-interval: 2000 acknowledge-mode: manual default-requeue-rejected: false template: mandatory: true # 觸發returnedMessage回調必須設置mandatory=true, 否則Exchange沒有找到Queue就會丟棄掉消息, 而不會觸發回調
在文件中增加
@Bean public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { RabbitTemplate rabbitTemplate = new RabbitTemplate(); rabbitTemplate.setConnectionFactory(connectionFactory); rabbitTemplate.setChannelTransacted(false); rabbitTemplate.setMandatory(true); rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() { @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if(ack){ log.info("消息發送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause); }else{ System.out.print("----"); } } }); rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() { @Override public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) { message.getMessageProperties().setDeliveryMode(MessageProperties.DEFAULT_DELIVERY_MODE); log.info("消息丟失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey, replyCode, replyText, message); } }); return rabbitTemplate; }
此兩步可以解決生產者丟失消息問題。
對於消費者丟失消息
設置手動確認。在消費代碼中try..catch.然後進行手動確認
@Component @RabbitListener(queues = "mall.order.cancel.ttl") public class CancelOrderReceiver { private static Logger LOGGER =LoggerFactory.getLogger(CancelOrderReceiver.class); /** * 默認情況下,如果沒有配置手動ACK, 那麼Spring Data AMQP 會在消息消費完畢後自動幫我們去ACK * 存在問題:如果報錯了,消息不會丟失,但是會無限循環消費,一直報錯,如果開啓了錯誤日誌很容易就吧磁盤空間耗完 * 解決方案:手動ACK,或者try-catch 然後在 catch 裏面將錯誤的消息轉移到其它的系列中去 * spring.rabbitmq.listener.simple.acknowledge-mode = manual */ @RabbitHandler public void cfgUserReceiveDealy(Long orderId, Message message, Channel channel) throws IOException { LOGGER.info("===============接收隊列接收消息===================="); LOGGER.info("接收時間:{},接受內容:{}", LocalDateTime.now(), orderId.toString(),message.getMessageProperties()); //通知 MQ 消息已被接收,可以ACK(從隊列中刪除)了 boolean isAck = true; try { int f = 1/0; } catch (Exception e) { isAck = false; LOGGER.error("============消費失敗,嘗試消息補發再次消費!=============="); LOGGER.error(e.getMessage()); //這裏最後一個參數是是否進入隊列。我這裏用法是死信隊列所以必須設置爲false。才能觸發死信 channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false); } if(isAck){ channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } } }