
RabbitMQ消息隊列異步解耦與業務削峰同步調用就像你打電話等對方接——對方不接你就一直卡著異步消息就像發微信——發完該干嘛干嘛對方有空了自然回你。一、消息隊列解決了什么問題在單體架構時代所有功能揉在一個項目里方法之間直接調用簡單粗暴。但一旦系統變大問題就來了異步處理用戶注冊后要發郵件、發短信、發優惠券……同步調用的話用戶得等半天體驗極差。丟到消息隊列里注冊接口秒回后續操作慢慢消費。應用解耦訂單系統直接調用庫存系統庫存掛了訂單也跟著掛。中間加個隊列訂單只管發消息庫存恢復了繼續消費即可。流量削峰秒殺場景瞬間涌入10萬請求數據庫直接被干趴。隊列做個緩沖消費者按自己的節奏處理系統穩如老狗。日志收集分布式系統中各服務把日志推到隊列由統一的日志服務消費存儲EFK/ELK的經典套路。二、RabbitMQ核心概念RabbitMQ的消息流轉模型如下Producer → Exchange → (Binding) → Queue → Consumer 生產者 交換機 綁定 隊列 消費者Producer生產者產生消息的應用程序Exchange交換機接收生產者發送的消息根據路由規則分發到隊列Queue隊列存放消息的緩沖區消息在這里排隊等消費Binding綁定交換機和隊列之間的關聯關系附帶路由鍵Consumer消費者從隊列中獲取消息并處理的應用程序三、交換機四種類型RabbitMQ提供了四種Exchange類型理解清楚就知道消息怎么路由了。3.1 Direct直連最簡單的模式消息的路由鍵routing key和綁定的鍵完全匹配消息才會被投遞到對應隊列。routing key order.create → 只匹配綁定 order.create 的隊列3.2 Fanout扇出廣播模式忽略路由鍵消息被投遞到與該交換機綁定的所有隊列。適合廣播通知場景。3.3 Topic主題支持通配符匹配靈活性最高*匹配一個單詞#匹配零個或多個單詞綁定鍵 order.* → 匹配 order.create、order.cancel不匹配 order.create.detail 綁定鍵 order.# → 匹配 order.create、order.create.detail 全都匹配3.4 Headers頭部不靠路由鍵而是根據消息頭headers中的鍵值對匹配。用的少了解即可。四、SpringBoot整合RabbitMQ4.1 引入依賴dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-amqp/artifactId/dependency4.2 yml配置spring:rabbitmq:host:127.0.0.1port:5672username:guestpassword:guest# 消息確認機制publisher-confirm-type:correlated# 發布確認publisher-returns:true# 消息返回listener:simple:acknowledge-mode:manual# 手動ACKprefetch:1# 每次拉取消息數4.3 隊列與交換機配置ConfigurationpublicclassRabbitMQConfig{// 隊列名稱publicstaticfinalStringEMAIL_QUEUEemail.queue;publicstaticfinalStringSMS_QUEUEsms.queue;publicstaticfinalStringORDER_EXCHANGEorder.exchange;publicstaticfinalStringORDER_ROUTING_KEYorder.notify;BeanpublicDirectExchangeorderExchange(){returnnewDirectExchange(ORDER_EXCHANGE,true,false);}BeanpublicQueueemailQueue(){returnnewQueue(EMAIL_QUEUE,true);}BeanpublicQueuesmsQueue(){returnnewQueue(SMS_QUEUE,true);}BeanpublicBindingemailBinding(QueueemailQueue,DirectExchangeorderExchange){returnBindingBuilder.bind(emailQueue).to(orderExchange).with(ORDER_ROUTING_KEY);}BeanpublicBindingsmsBinding(QueuesmsQueue,DirectExchangeorderExchange){returnBindingBuilder.bind(smsQueue).to(orderExchange).with(ORDER_ROUTING_KEY);}}五、發送消息RabbitTemplateServicepublicclassOrderService{AutowiredprivateRabbitTemplaterabbitTemplate;publicvoidcreateOrder(OrderDTOorderDTO){// 1. 保存訂單數據庫操作省略// ...// 2. 異步發送通知消息StringmsgJSON.toJSONString(orderDTO);rabbitTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE,RabbitMQConfig.ORDER_ROUTING_KEY,msg);// 3. 直接返回不等郵件/短信發送完成return;}}六、接收消息RabbitListenerComponentpublicclassEmailConsumer{RabbitListener(queuesRabbitMQConfig.EMAIL_QUEUE)RabbitHandlerpublicvoidreceive(Stringmessage,Channelchannel,MessagemessageObj)throwsIOException{longdeliveryTagmessageObj.getMessageProperties().getDeliveryTag();try{OrderDTOorderJSON.parseObject(message,OrderDTO.class);// 發送郵件邏輯System.out.println(發送郵件到order.getEmail());// 手動確認channel.basicAck(deliveryTag,false);}catch(Exceptione){// 消費失敗拒絕并重新入隊channel.basicNack(deliveryTag,false,true);}}}短信消費者結構同理監聽SMS_QUEUE即可。一個交換機綁定了兩個隊列同一條消息會同時投遞到郵件隊列和短信隊列實現并行處理。七、消息可靠性保障消息從生產到消費要經過多個環節任何一個環節都可能丟消息。7.1 生產者確認機制publisher-confirm-type:correlated# 異步確認性能好rabbitTemplate.setConfirmCallback((correlationData,ack,cause)-{if(!ack){System.err.println(消息未到達Exchange原因cause);// 記錄日志重發等處理}});7.2 消費者手動ACK默認是自動確認auto消息一拿到就標記消費成功但如果業務代碼報異常消息就丟了。改為手動確認manual業務成功后調basicAck失敗調basicNack。八、死信隊列消息變成死信的三種情況消息被消費者rejectbasicReject/basicNack且不重新入隊消息TTL過期隊列或消息設置了過期時間隊列達到最大長度新消息被擠出去死信隊列的配置思路給正常隊列綁定一個死信交換機DLX消息變成死信后自動轉發到DLX再由DLX路由到死信隊列。BeanpublicQueuenormalQueue(){MapString,ObjectargsnewHashMap();args.put(x-message-ttl,60000);// 消息60秒過期args.put(x-dead-letter-exchange,dlx.exchange);args.put(x-dead-letter-routing-key,dlx.routing.key);returnnewQueue(normal.queue,true,false,false,args);}死信隊列常用于延遲任務消息過期→死信→消費、失敗消息重試、訂單超時取消等場景。九、常見問題與解決方案9.1 消息重復消費冪等性網絡抖動導致ACK沒及時到達RabbitMQ會重投消息消費者就重復處理了。解決方案業務唯一鍵校驗消費前查數據庫/Redis已處理則直接ACK跳過樂觀鎖update語句加where status 0條件Redis分布式鎖setnx保證同一消息只處理一次publicvoidreceive(Stringmessage){StringmsgIdextractMsgId(message);// Redis標記已處理則跳過BooleanisNewredisTemplate.opsForValue().setIfAbsent(msg:processed:msgId,1,24,TimeUnit.HOURS);if(Boolean.FALSE.equals(isNew)){return;// 已處理過}// 正常消費邏輯}9.2 消息積壓處理消費速度跟不上生產速度隊列堆積越來越多的消息。應對策略臨時擴容消費者增加消費者實例數量批量消費一個消費者一次拉取多條消息處理消息轉存緊急將積壓消息轉存到另一個隊列后續慢慢消費根因排查消費者是不是有慢查詢是不是依賴的外部服務超時了RabbitMQ用好了就是系統穩定性的護城河用不好就是給自己挖坑。把可靠性保障和冪等性設計到位消息隊列才能真正發揮價值。