RabbitMQ 消息隊列:從入門到Spring Boot實戰

RabbitMQ 作為一款開源的、基于 AMQP(Advanced Message Queuing Protocol)協議實現的消息代理,憑借其強大的功能、靈活的路由機制以及出色的性能,在業界得到了廣泛的應用。無論是處理高并發訂單、異步通知、日志收集還是系統解耦,RabbitMQ 都能發揮其獨特的作用。

1. RabbitMQ 核心概念

RabbitMQ 的強大功能離不開其背后一系列精心設計的核心概念。理解這些概念是掌握 RabbitMQ 的基礎,也是構建高效、可靠消息系統的關鍵。

1.1 生產者 (Producer) 與消費者 (Consumer)

在消息隊列的世界里,生產者和消費者是兩個最基本的角色,它們共同構成了消息流轉的起點和終點。

  • 生產者 (Producer):生產者是消息的創建者和發送者。它負責將業務數據封裝成消息,并將其發送到 RabbitMQ 消息代理(Broker)。生產者通常不關心消息最終會被哪個消費者處理,它只負責將消息可靠地投遞到 RabbitMQ。

  • 消費者 (Consumer):消費者是消息的接收者和處理者。它從 RabbitMQ 的隊列中獲取消息,并根據業務邏輯對消息進行相應的處理。一個隊列可以有多個消費者,它們之間可以形成競爭關系(競爭消費者模式),也可以形成廣播關系(發布/訂閱模式)。

消息在生產者和消費者之間流轉,實現了業務邏輯的解耦。生產者無需知道消費者是誰,消費者也無需知道消息來自哪個生產者,它們之間通過 RabbitMQ 這一中間件進行通信,大大提高了系統的靈活性和可維護性。

1.2 消息隊列 (Queue)

消息隊列是 RabbitMQ 的核心組件之一,它是消息的“家”,所有發送到 RabbitMQ 的消息都會被存儲在隊列中,直到被消費者取走。隊列在 RabbitMQ 中扮演著重要的角色,它確保了消息的持久化存儲和有序傳輸。

消息隊列具有以下幾個重要屬性:

  • 持久性 (Durability):當隊列被聲明為持久化時,即使 RabbitMQ 服務重啟,該隊列以及其中未被消費的消息也不會丟失。這對于確保消息的可靠性至關重要。在實際應用中,為了避免消息丟失,通常會將隊列設置為持久化。

  • 自動刪除 (Auto-delete):如果一個隊列被設置為自動刪除,那么當最后一個消費者斷開連接后,該隊列會自動被刪除。這種隊列適用于臨時性的消息處理場景,例如某些一次性任務的隊列。

  • 惰性 (Lazy):惰性隊列是 RabbitMQ 3.6 版本引入的新特性。當隊列被聲明為惰性時,消息會盡可能地寫入磁盤,而不是一直保存在內存中。這有助于減少內存消耗,尤其是在處理大量消息堆積的場景下。但是,從磁盤讀取消息會帶來一定的性能開銷。

  • 排他性 (Exclusive):排他隊列是與連接綁定的。當聲明排他隊列的連接斷開時,該隊列會自動被刪除。排他隊列只能被聲明它的連接訪問,其他連接無法訪問。這種隊列通常用于客戶端獨占的臨時隊列。

1.3 交換機 (Exchange)

交換機是 RabbitMQ 消息路由的核心組件。生產者發送消息時,并不是直接將消息發送到隊列,而是發送到交換機。交換機根據其類型和路由規則,將消息路由到一個或多個隊列中,或者直接丟棄消息。交換機是消息路由的“交通樞紐”。

RabbitMQ 提供了四種主要的交換機類型,每種類型都有其獨特的路由行為:

  • Direct Exchange (直連交換機):直連交換機是最簡單的交換機類型。它根據消息的 routing key 進行精確匹配。當消息的 routing key 與隊列綁定的 binding key 完全一致時,消息才會被路由到該隊列。直連交換機適用于點對點通信或需要精確路由的場景。

  • Topic Exchange (主題交換機):主題交換機在直連交換機的基礎上增加了模式匹配的功能。它允許 binding key 使用通配符進行模糊匹配。其中,* 匹配一個單詞,# 匹配零個或多個單詞。主題交換機非常靈活,適用于日志系統、事件分發等需要根據不同主題訂閱消息的場景。

  • Headers Exchange (首部交換機):首部交換機不依賴 routing key 進行路由,而是根據消息的 headers 屬性進行匹配。生產者在發送消息時,可以在消息的 headers 中添加自定義屬性。隊列在綁定到首部交換機時,可以指定一組 headers 屬性和匹配規則(例如 allany),只有當消息的 headers 滿足匹配規則時,消息才會被路由到該隊列。首部交換機提供了更靈活的路由方式,適用于復雜的業務場景。

  • Fanout Exchange (扇形交換機):扇形交換機是最“粗暴”的交換機類型。它會將接收到的所有消息廣播到所有綁定到它的隊列中,完全忽略消息的 routing key。扇形交換機適用于發布/訂閱模式,例如廣播通知、日志分發等場景。

1.4 綁定 (Binding)

綁定是連接交換機和隊列的橋梁。它告訴交換機,當滿足特定條件時,應該將消息路由到哪個隊列。綁定關系是 RabbitMQ 路由機制的核心,它定義了消息從交換機到隊列的路徑。

在創建綁定時,通常會指定一個 binding keybinding key 的作用取決于交換機的類型:

  • 對于 Direct Exchange,binding key 必須與消息的 routing key 完全匹配。
  • 對于 Topic Exchange,binding key 可以包含通配符,與消息的 routing key 進行模式匹配。
  • 對于 Fanout Exchange,binding key 會被忽略,因為所有消息都會被廣播。
  • 對于 Headers Exchange,binding key 不起作用,路由基于消息的 headers

1.5 消息確認 (Acknowledgement)

消息確認是 RabbitMQ 確保消息可靠性的重要機制。當消費者從隊列中獲取消息并成功處理后,會向 RabbitMQ 發送一個確認(ACK)信號。RabbitMQ 收到確認信號后,才會將該消息從隊列中徹底刪除。如果在消費者處理消息過程中發生異常或消費者崩潰,未發送確認信號,RabbitMQ 會認為消息未被成功處理,會將消息重新投遞給其他消費者(或同一消費者),從而保證消息不會丟失。

消息確認分為兩種模式:

  • 自動確認 (Auto Acknowledge):在這種模式下,RabbitMQ 會在消息發送給消費者后立即將其從隊列中刪除,而不管消費者是否真正處理了消息。這種模式簡單方便,但存在消息丟失的風險,不適用于對消息可靠性要求高的場景。

  • 手動確認 (Manual Acknowledge):在這種模式下,消費者在處理完消息后,需要顯式地向 RabbitMQ 發送確認信號。手動確認提供了更高的消息可靠性,因為只有當消費者確認消息已成功處理后,消息才會被刪除。手動確認又分為肯定確認(basicAck)、否定確認(basicNack)和拒絕消息(basicReject)等操作,可以根據業務需求靈活控制消息的處理狀態。

2. RabbitMQ 常用工作模式

RabbitMQ 提供了多種靈活的工作模式,以適應不同的消息通信需求。

2.1 簡單模式 (Simple Mode)

簡單模式,顧名思義,是最基礎的消息通信模式,也被稱為“Hello World”模式。它由一個生產者、一個隊列和一個消費者組成。

  • 特點:生產者將消息發送到隊列,消費者從隊列中獲取消息并處理。消息按照先進先出(FIFO)的順序進行處理。這種模式實現了最簡單的點對點通信。
  • 應用場景:適用于簡單的消息傳遞,例如發送一次性通知、處理單個任務等。

2.2 工作隊列模式 (Work Queues Mode)

工作隊列模式(也稱為任務隊列或競爭消費者模式)旨在解決單個消費者處理消息速度慢的問題。在這種模式下,一個生產者將消息發送到一個隊列,但有多個消費者同時監聽該隊列,共同競爭消息。

  • 特點
    • 負載均衡:RabbitMQ 默認采用輪詢(Round-robin)的方式將消息分發給不同的消費者,從而實現消息的負載均衡。
    • 消息確認:為了確保消息不丟失,消費者在處理完消息后需要發送確認信號。如果消費者在處理過程中崩潰,未確認的消息會被重新投遞給其他消費者。
  • 應用場景:適用于處理耗時任務的場景,例如圖片處理、視頻轉碼、郵件發送等。通過增加消費者數量,可以提高系統的吞吐量和處理能力。

2.3 發布/訂閱模式 (Publish/Subscribe Mode)

發布/訂閱模式(Pub/Sub)實現了消息的廣播。在這種模式下,消息不再直接發送到隊列,而是發送到 Fanout Exchange。所有綁定到該交換機的隊列都會收到消息,然后這些隊列再將消息分發給各自的消費者。

  • 特點
    • 廣播:一個消息可以被多個消費者同時接收和處理。
    • 解耦:生產者和消費者之間完全解耦,生產者無需知道有多少個消費者,消費者也無需知道消息來自哪個生產者。
  • 應用場景:適用于需要將同一消息發送給多個訂閱者的場景,例如日志系統(所有日志都發送給所有日志處理服務)、事件通知(用戶注冊成功后通知所有相關服務)等。

2.4 路由模式 (Routing Mode)

路由模式允許消息根據 routing key 進行有選擇地路由。在這種模式下,消息發送到 Direct Exchange,隊列在綁定到交換機時會指定一個 binding key。只有當消息的 routing key 與隊列的 binding key 完全匹配時,消息才會被路由到該隊列。

  • 特點
    • 選擇性接收:消費者可以根據自己感興趣的 routing key 來接收消息。
    • 靈活路由:通過定義不同的 routing key,可以實現消息的精細化路由。
  • 應用場景:適用于需要根據消息的特定屬性進行分類處理的場景,例如日志系統(根據日志級別路由到不同的處理服務,如 error 級別的日志發送到錯誤處理服務,info 級別的日志發送到信息記錄服務)。

2.5 主題模式 (Topics Mode)

主題模式是路由模式的增強版,它允許 routing keybinding key 使用通配符進行模式匹配。消息發送到 Topic Exchange,隊列在綁定到交換機時會指定一個包含通配符的 binding key

  • 特點
    • 更靈活的匹配* 匹配一個單詞,# 匹配零個或多個單詞。這使得消息路由更加靈活和強大。
    • 多條件訂閱:消費者可以訂閱符合特定模式的消息。
  • 應用場景:適用于需要根據多個條件進行消息過濾和訂閱的場景,例如股票行情系統(用戶可以訂閱 *.stock.us 來獲取所有美國股票信息,或者訂閱 ibm.# 來獲取所有 IBM 相關的消息)。

3. Spring Boot 整合 RabbitMQ 實戰

Spring Boot 極大地簡化了 Spring 應用的開發,包括與各種消息中間件的集成。通過 Spring AMQP 模塊,我們可以非常方便地在 Spring Boot 項目中集成 RabbitMQ,實現消息的發送和接收。

3.1 環境準備

首先,我們需要創建一個 Spring Boot 項目,并在 pom.xml 中引入 RabbitMQ 相關的 Maven 依賴。此外,確保本地或遠程環境中已經安裝并運行了 RabbitMQ 服務。

3.1.1 Maven 依賴引入

pom.xml 文件中添加以下依賴:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version> <!-- 根據您的Spring Boot版本調整 --><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>rabbitmq-spring-boot-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>rabbitmq-spring-boot-demo</name><description>Demo project for Spring Boot and RabbitMQ</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

3.1.2 RabbitMQ 安裝與配置

如果您還沒有安裝 RabbitMQ,可以通過 Docker 或直接下載安裝包進行安裝。這里以 Docker 為例,快速啟動一個 RabbitMQ 實例:

docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:management

上述命令會啟動一個 RabbitMQ 容器,并暴露 5672 端口(AMQP 協議端口)和 15672 端口(管理界面端口)。您可以通過瀏覽器訪問 http://localhost:15672,使用默認的 guest/guest 賬號登錄管理界面。

3.2 配置文件

src/main/resources/application.yml (或 application.properties) 中配置 RabbitMQ 連接信息:

spring:rabbitmq:host: localhostport: 5672username: guestpassword: guestvirtual-host: /listener:simple:acknowledge-mode: manual # 消費者手動確認消息

這里我們配置了 RabbitMQ 的連接地址、端口、用戶名、密碼和虛擬主機。特別地,我們將消費者消息確認模式設置為 manual,以便在代碼中手動確認消息,確保消息的可靠性。

3.3 生產者實現

生產者負責將消息發送到 RabbitMQ 的交換機。我們將演示如何使用 RabbitTemplate 發送消息到不同類型的交換機。

3.3.1 配置交換機和隊列

為了方便管理和配置,我們可以在 Spring Boot 配置類中定義交換機、隊列和綁定關系。這里以 Direct Exchange、Topic Exchange 和 Fanout Exchange 為例。

package com.example.rabbitmqspringbootdemo.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitMQConfig {// Direct Exchange 相關配置public static final String DIRECT_EXCHANGE = "direct.exchange";public static final String DIRECT_QUEUE = "direct.queue";public static final String DIRECT_ROUTING_KEY = "direct.routing.key";@Beanpublic DirectExchange directExchange() {return new DirectExchange(DIRECT_EXCHANGE, true, false);}@Beanpublic Queue directQueue() {return new Queue(DIRECT_QUEUE, true);}@Beanpublic Binding directBinding(Queue directQueue, DirectExchange directExchange) {return BindingBuilder.bind(directQueue).to(directExchange).with(DIRECT_ROUTING_KEY);}// Topic Exchange 相關配置public static final String TOPIC_EXCHANGE = "topic.exchange";public static final String TOPIC_QUEUE_A = "topic.queue.a";public static final String TOPIC_QUEUE_B = "topic.queue.b";public static final String TOPIC_ROUTING_KEY_A = "topic.#.a"; // 匹配 topic.x.a, topic.y.z.apublic static final String TOPIC_ROUTING_KEY_B = "topic.b.*"; // 匹配 topic.b.x@Beanpublic TopicExchange topicExchange() {return new TopicExchange(TOPIC_EXCHANGE, true, false);}@Beanpublic Queue topicQueueA() {return new Queue(TOPIC_QUEUE_A, true);}@Beanpublic Queue topicQueueB() {return new Queue(TOPIC_QUEUE_B, true);}@Beanpublic Binding topicBindingA(Queue topicQueueA, TopicExchange topicExchange) {return BindingBuilder.bind(topicQueueA).to(topicExchange).with(TOPIC_ROUTING_KEY_A);}@Beanpublic Binding topicBindingB(Queue topicQueueB, TopicExchange topicExchange) {return BindingBuilder.bind(topicQueueB).to(topicExchange).with(TOPIC_ROUTING_KEY_B);}// Fanout Exchange 相關配置public static final String FANOUT_EXCHANGE = "fanout.exchange";public static final String FANOUT_QUEUE_1 = "fanout.queue.1";public static final String FANOUT_QUEUE_2 = "fanout.queue.2";@Beanpublic FanoutExchange fanoutExchange() {return new FanoutExchange(FANOUT_EXCHANGE, true, false);}@Beanpublic Queue fanoutQueue1() {return new Queue(FANOUT_QUEUE_1, true);}@Beanpublic Queue fanoutQueue2() {return new Queue(FANOUT_QUEUE_2, true);}@Beanpublic Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);}@Beanpublic Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);}
}

3.3.2 消息發送服務

創建一個生產者服務,使用 RabbitTemplate 發送消息。RabbitTemplate 是 Spring AMQP 提供的核心組件,用于發送和接收消息。

package com.example.rabbitmqspringbootdemo.producer;import com.example.rabbitmqspringbootdemo.config.RabbitMQConfig;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class MessageProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendDirectMessage(String message) {rabbitTemplate.convertAndSend(RabbitMQConfig.DIRECT_EXCHANGE, RabbitMQConfig.DIRECT_ROUTING_KEY, message);System.out.println("Direct Message Sent: " + message);}public void sendTopicMessageA(String message) {rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE, "topic.order.a", message);System.out.println("Topic Message A Sent: " + message);}public void sendTopicMessageB(String message) {rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE, "topic.user.b.register", message);System.out.println("Topic Message B Sent: " + message);}public void sendFanoutMessage(String message) {rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE, "", message); // Fanout Exchange 忽略 routing keySystem.out.println("Fanout Message Sent: " + message);}
}

3.3.3 測試生產者

您可以通過一個簡單的 Controller 來觸發消息發送:

package com.example.rabbitmqspringbootdemo.controller;import com.example.rabbitmqspringbootdemo.producer.MessageProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController {@Autowiredprivate MessageProducer messageProducer;@GetMapping("/sendDirect")public String sendDirectMessage(@RequestParam String msg) {messageProducer.sendDirectMessage(msg);return "Direct message sent: " + msg;}@GetMapping("/sendTopicA")public String sendTopicMessageA(@RequestParam String msg) {messageProducer.sendTopicMessageA(msg);return "Topic message A sent: " + msg;}@GetMapping("/sendTopicB")public String sendTopicMessageB(@RequestParam String msg) {messageProducer.sendTopicMessageB(msg);return "Topic message B sent: " + msg;}@GetMapping("/sendFanout")public String sendFanoutMessage(@RequestParam String msg) {messageProducer.sendFanoutMessage(msg);return "Fanout message sent: " + msg;}
}

3.4 消費者實現

消費者通過 @RabbitListener 注解來監聽指定隊列的消息。我們將演示如何接收不同隊列的消息,并進行手動確認。

package com.example.rabbitmqspringbootdemo.consumer;import com.example.rabbitmqspringbootdemo.config.RabbitMQConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
public class MessageConsumer {@RabbitListener(queues = RabbitMQConfig.DIRECT_QUEUE)public void receiveDirectMessage(String message, Channel channel, Message msg) throws IOException {try {System.out.println("Received Direct Message: " + message);// 模擬業務處理// int i = 1/0; // 模擬異常channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);System.out.println("Direct Message Acknowledged.");} catch (Exception e) {System.err.println("Error processing Direct Message: " + e.getMessage());// 拒絕消息,并重新入隊channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);System.err.println("Direct Message Nacked and Requeued.");}}@RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE_A)public void receiveTopicMessageA(String message, Channel channel, Message msg) throws IOException {try {System.out.println("Received Topic Message A: " + message);channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);System.out.println("Topic Message Acknowledged.");} catch (Exception e) {System.err.println("Error processing Topic Message A: " + e.getMessage());channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);System.err.println("Topic Message Nacked and Requeued.");}}@RabbitListener(queues = RabbitMQConfig.TOPIC_QUEUE_B)public void receiveTopicMessageB(String message, Channel channel, Message msg) throws IOException {try {System.out.println("Received Topic Message B: " + message);channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);System.out.println("Topic Message B Acknowledged.");} catch (Exception e) {System.err.println("Error processing Topic Message B: " + e.getMessage());channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);System.err.println("Topic Message Nacked and Requeued.");}}@RabbitListener(queues = RabbitMQConfig.FANOUT_QUEUE_1)public void receiveFanoutMessage1(String message, Channel channel, Message msg) throws IOException {try {System.out.println("Received Fanout Message 1: " + message);channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);System.out.println("Fanout Message 1 Acknowledged.");} catch (Exception e) {System.err.println("Error processing Fanout Message 1: " + e.getMessage());channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);System.err.println("Fanout Message 1 Nacked and Requeued.");}}@RabbitListener(queues = RabbitMQConfig.FANOUT_QUEUE_2)public void receiveFanoutMessage2(String message, Channel channel, Message msg) throws IOException {try {System.out.println("Received Fanout Message 2: " + message);channel.basicAck(msg.getMessageProperties().getDeliveryTag(), false);System.out.println("Fanout Message 2 Acknowledged.");} catch (Exception e) {System.err.println("Error processing Fanout Message 2: " + e.getMessage());channel.basicNack(msg.getMessageProperties().getDeliveryTag(), false, true);System.err.println("Fanout Message 2 Nacked and Requeued.");}}
}

3.5 消息確認機制 (ACK)

在上述消費者代碼中,我們通過 Channel 對象實現了消息的手動確認。這是確保消息可靠性的關鍵步驟。

  • channel.basicAck(deliveryTag, multiple):用于肯定確認消息。deliveryTag 是消息的唯一標識,multiple 參數表示是否批量確認。設置為 false 表示只確認當前消息。
  • channel.basicNack(deliveryTag, multiple, requeue):用于否定確認消息。requeue 參數表示是否將消息重新放回隊列。設置為 true 表示重新入隊,false 表示丟棄或發送到死信隊列(如果配置了)。
  • channel.basicReject(deliveryTag, requeue):與 basicNack 類似,但不支持批量操作。

通過手動確認機制,我們可以根據業務處理結果靈活地控制消息的生命周期。例如,在業務處理成功后進行 basicAck,在處理失敗時進行 basicNack 并選擇是否重新入隊,從而避免消息丟失或重復消費的問題。

4. 總結

RabbitMQ 作為一款成熟穩定的消息中間件,在分布式系統、微服務架構中扮演著舉足輕重的角色。它不僅能夠幫助我們實現系統解耦、異步通信,還能有效應對高并發、削峰填谷等挑戰,提升系統的整體性能和穩定性。

在實際項目中,除了本文介紹的基本概念和工作模式,RabbitMQ 還提供了許多高級特性,例如死信隊列(Dead Letter Exchange)、延遲隊列、消息優先級等,這些特性可以幫助我們構建更加健壯和靈活的消息系統。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88403.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88403.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88403.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

代賬行業數字化破局:從“知道”到“做到”,三步走穩贏!

認知&#xff01;降本&#xff01;增收&#xff01;數字化&#xff01;——這不僅是口號&#xff0c;更是代賬行業在激烈競爭和時代變化中生存發展的關鍵。很多代賬同行其實都明白趨勢&#xff0c;也知道大概該怎么做。但問題卡在第一步&#xff1a;不知道怎么開始&#xff0c;…

Mac 電腦crontab執行定時任務【Python 實戰】

1、crontab -e 編輯定時任務列表 crontab -e查看當前定時任務列表,長按 i 編輯,編輯完之后按 esc 退出編輯,然后輸入:wq 保存并提出。 如下: (base) charles@zl ~ % crontab -e58 15 * * * /Library/Frameworks/Python.framework/Versions/3.8/bin/python3 /Users/charle…

go go go 出發咯 - go web開發入門系列(三) 項目基礎框架搭建與解讀

go go go 出發咯 - go web開發入門系列&#xff08;三&#xff09; 項目基礎框架搭建與解讀 往期回顧 go go go 出發咯 - go web開發入門系列&#xff08;一&#xff09; helloworldgo go go 出發咯 - go web開發入門系列&#xff08;二&#xff09; Gin 框架實戰指南 前言 如…

【字節跳動】數據挖掘面試題0014:SQL中count(1), count(*), count(列)區別

文章大綱SQL 中 count(1)、count(*)、count(某列) 的區別一、核心定義與行為差異二、示例說明差異三、性能差異與優化四、適用場景建議五、面試應答要點六、索引掃描與全表掃描1. 索引掃描的觸發條件2. 全表掃描的適用場景3. 常見面試問題點Q1&#xff1a;索引掃描一定比全表掃…

Linux面試問題-軟件測試

1、你在上一家公司常用的Linux命令有哪些&#xff1f;答&#xff1a;使用vim/vi編輯文件&#xff0c;使用cat&#xff0c;more,less&#xff0c;head查看文件&#xff0c;使用grep過濾日志中的error,使用ps查看進程&#xff0c;使用top查看實時進程&#xff0c;netstat查看端口…

時序數據庫的存儲之道:從數據特性看技術要點

時序數據的獨特挑戰時序數據(Time-Series Data)是指按時間順序記錄的一系列數據點&#xff0c;在物聯網、金融、工業監控等領域無處不在。與傳統數據相比&#xff0c;時序數據具有幾個鮮明特點&#xff1a;時間導向性&#xff1a;每個數據點都帶有精確的時間戳高寫入量&#xf…

【vim中替換】

vim中替換1 : s/在Vim中經常高頻使用到的命令&#xff1a;1 : s/ &#xff1a;s 命令的基本語法是 :[range]s/{pattern}/{string}/[flags]&#xff0c;其中&#xff1a; ? [range] 是可選的范圍&#xff0c;用于指定替換的行范圍。例如&#xff0c;% 表示全文&#xff0c;10,…

Qt實戰:使用QSqlDatabase連接MySQL,并實現增刪改查

文章目錄一、創建數據表二、連接MySQL數據庫三、封裝成一個完整的輕量級 ORM 風格類四、實現派生具體模型類五、支持多線程連接池 ORM 事務封裝一、創建數據表 數據庫名&#xff1a; 我們先創建一個數據庫&#xff0c;名字叫 game_db&#xff1a; CREATE DATABASE IF NOT E…

Python腳本保護工具庫之pyarmor使用詳解

概要 PyArmor是一個專門為Python代碼提供加密保護的第三方庫,旨在解決Python源代碼易被反編譯和泄露的安全問題。作為一種動態代碼保護工具,PyArmor能夠對Python腳本進行混淆和加密處理,有效防止源代碼被惡意獲取、分析或篡改。該庫特別適用于商業軟件開發、知識產權保護和…

倉頡編程語言:從入門到精通

為啥要瞅瞅倉頡這玩意兒&#xff1f; 有一說一&#xff0c;現在的編程語言多得跟米一樣&#xff0c;對吧&#xff1f;那一門新語言想火&#xff0c;沒點絕活兒肯定不行。倉頡&#xff08;Cangjie&#xff09;這哥們兒&#xff0c;是華為搞出來的新玩意兒&#xff0c;靜態編譯的…

線性探針是什么:是一種用于探測神經網絡中特定特征的工具

線性探針是什么 線性探針是一種在機器學習和相關領域廣泛應用的技術,用于評估預訓練模型特征、檢測數據中的特定序列等。在不同的應用場景下,線性探針有著不同的實現方式和作用: 評估預訓練模型特征:在機器學習中,線性探針是一種評估預訓練模型“特征遷移能力”的標準化方…

【論文閱讀】Few-Shot PPG Signal Generation via Guided Diffusion Models

從少量樣本數據選擇到后處理的整體框架。首先,擴散模型在N樣本數據集和指導下的訓練。接著,模型生成一個增強的數據集,并進一步優化以提高保真度。最后,這些合成數據與少量樣本訓練數據集結合,用于基準模型的訓練和評估。數據分布從最初的紅色變為保真度增強的藍色,這表明…

CentOS-7的“ifupdown“與Debian的“ifupdown“對比 筆記250706

CentOS-7的"ifupdown"與Debian的"ifupdown"對比 筆記250706 CentOS 7 和 Debian 的 ifupdown 工具名稱相同&#xff0c;但在實現機制、配置文件語法和系統集成上存在顯著差異。以下是核心對比分析&#xff1a; ?? 一、核心差異概覽 對比維度CentOS 7De…

架構如傳承:技術長河中的可持續樂章

代碼結構&#xff1a;協作基石 在軟件開發的世界里&#xff0c;代碼結構就如同建筑的框架&#xff0c;支撐著整個項目的運行。想象一下&#xff0c;你加入了一個新的開發團隊&#xff0c;接手一個已經有一定規模的項目。當你打開代碼庫&#xff0c;看到的是一團亂麻般的代碼&a…

Ubuntu22.04更新Openssh至9.9p2無法正常連接,報錯解決

Ubuntu22.04更新Openssh至9.9p2無法正常連接&#xff0c;報錯解決 1.報錯信息如下所示ExecStart/usr/sbin/sshd -D $SSHD_OPTS (codeexited, status255/EXCEPTION)2.這通常說明 SSH 配置文件存在語法錯誤、缺失關鍵文件&#xff0c;或者端口被占用等問題。 3.檢查配置文件是否有…

基于小程序的智能停車管理系統設計與開發

項目介紹 本課程演示的是一款基于小程序的智能停車管理系統設計與開發&#xff0c;主要針對計算機相關專業的正在做畢設的學生與需要項目實戰練習的 Java 學習者。 1.包含&#xff1a;項目源碼、項目文檔、數據庫腳本、軟件工具等所有資料 2.帶你從零開始部署運行本套系統 3…

多模態大語言模型arxiv論文略讀(155)

Panther: Illuminate the Sight of Multimodal LLMs with Instruction-Guided Visual Prompts ?? 論文標題&#xff1a;Panther: Illuminate the Sight of Multimodal LLMs with Instruction-Guided Visual Prompts ?? 論文作者&#xff1a;Honglin Li, Yuting Gao, Chengl…

SAP ERP與Oracle EBS對比,兩個ERP系統有什么區別?

據統計&#xff0c;2024年中國ERP軟件市場規模預計突破210億元&#xff0c;其中SAP和Oracle占據第一梯隊&#xff0c;共占國內ERP市場45%以上的份額&#xff0c;在高端市場尤其顯著。SAP和Oracle作為ERP行業的兩大巨頭&#xff0c;具體有什么區別呢&#xff1f;SAP是什么&#…

網絡安全之RCE分析與利用詳情

Gogs背景介紹Gogs&#xff08;Go Git Service&#xff09;是一款用Go語言編寫的輕量級、開源的Git倉庫托管系統。它的設計目標是讓搭建和維護Git服務變得簡單、快速&#xff0c;同時提供類似GitHub的功能&#xff0c;但對資源消耗更少&#xff0c;適合個人或者小型團隊使用&…

OpenCV圖片操作100例:從入門到精通指南(2)

接上篇&#xff0c;本文將繼續分享OpenCV實用技巧&#xff0c;涵蓋圖像處理、目標檢測、3D視覺等進階領域&#xff01;六、圖像變換進階17. 圖像金字塔# 高斯金字塔下采樣 smaller cv2.pyrDown(img)# 高斯金字塔上采樣 larger cv2.pyrUp(img)用于多尺度圖像處理&#xff0c;構…