RabbitMQ入門介紹以及安裝使用

一、RabbitMQ初識

RabbitMQ是一個實現了高級消息隊列協議(AMQP的消息代理(也叫消息中間件),它接受並轉發消息。它可以幫你處理一些邏輯的事務,從而進行解耦,比如用戶註冊落庫之後,還需要發送郵件驗證、需要發送新人紅包等等事情,就可以交給中間件去做。也可以把它當成一個郵局:當你想郵寄信件的時候,你會把信件放在投遞箱中,並確信郵遞員最終會將信件送到收件人的手裏。在這個例子中,RabbitMQ就相當與投遞箱、郵局和郵遞員。

1.1 AMQP協議

AMQP,即Advanced Message Queuing Protocol,一個提供統一消息服務的應用層標準高級消息隊列協議,是應用層協議的一個開放標準,爲面向消息的中間件設計。基於此協議的客戶端與消息中間件可傳遞消息,並不受客戶端/中間件產品、不同的開發語言等條件的限制。

1.2 典型應用場景

  1. 跨系統的異步通信 ,異步、解耦、削峯。
  2. 應用內的同步變成異步 秒殺:自己發送給自己
  3. 基於Pub/Sub模型實現的事件驅動 ,摒棄ELT(比如全量 同步商戶數據); 摒棄API(比如定時增量獲取用戶、獲取產品,變成增量廣播)。
  4. 利用RabbitMQ實現事務的最終一致性

1.3 RabbitMQ的特性

RabbitMQ使用Erlang語言編寫,使用Mnesia數據庫存儲消息。

  1. 可靠性(Reliability) RabbitMQ 使用一些機制來保證可靠性,如持久化、傳輸確認、發佈確認。
  2. 靈活的路由(Flexible Routing) 在消息進入隊列之前,通過 Exchange 來路由消息的。對於典型的路由功能,RabbitMQ 已經提供了一些內置的 Exchange 來實現。針對更復雜的路由功能,可以將多個 Exchange 綁定在 一起,也通過插件機制實現自己的 Exchange 。
  3. 消息集羣(Clustering) 多個 RabbitMQ 服務器可以組成一個集羣,形成一個邏輯 Broker 。
  4. 高可用(Highly Available Queues) 隊列可以在集羣中的機器上進行鏡像,使得在部分節點出問題的情況下
    隊列仍然可用。
  5. 多種協議(Multi-protocol) RabbitMQ 支持多種消息隊列協議,比如 AMQP、STOMP、MQTT 等等。
  6. 多語言客戶端(Many Clients) RabbitMQ 幾乎支持所有常用語言,比如 Java、.NET、Ruby、PHP、C#、 JavaScript 等等。
  7. 管理界面(Management UI) RabbitMQ 提供了一個易用的用戶界面,使得用戶可以監控和管理消息、集羣 中的節點。
  8. 插件機制(Plugin System) RabbitMQ提供了許多插件,以實現從多方面擴展,當然也可以編寫自己的插件。

1.4 工作模型

在這裏插入圖片描述

  1. Broker
    我們要使用 RabbitMQ 來收發消息,必須要安裝一個 RabbitMQ 的服務,可以安裝在 Windows 上面也可以安裝在 Linux 上面,默認是 5672 的端口。這臺 RabbitMQ 的服務器我們把它叫做 Broker, MQ 服務器幫助我們做的事情就是存儲、轉發消息。
  2. Connection
    無論是生產者發送消息,還是消費者接收消息,都必須要跟 Broker 之間建立一個連接,這個連接是一個 TCP 的長連接。
  3. Channel
    如果所有的生產者發送消息和消費者接收消息,都直接創建和釋放 TCP 長連接的話,對於 Broker 來說肯定會造成很大的性能損耗,因爲 TCP 連接是非常寶貴的資源,創建和釋放也要消耗時間。所以在 AMQP 裏面引入了 Channel 的概念,它是一個虛擬的連接。我們把它翻譯成通道,或者消息信道。這樣我們就可以在保持的 TCP 長連接裏面去創建和釋放Channel,大大了減少了資源消耗。另外一個需要注意的是,Channel 是 RabbitMQ 原生 API 裏面的最重要的編程接口,也就是說我們定義交換機、隊列、綁定關係,發送消息消費消息,調用的都是 Channel 接口上的方法。
  4. Queue
    在其他一些 MQ 裏面,比如ActiveMQ ,消息都是發送到隊列上的。隊列是真正用來存儲消息的,是一個獨立運行的進程,有自己的數據庫。消費者獲取消息有兩種模式,一種是 Push 模式,只要生產者發到服務器,就馬上推送給消費者。另一種是 Pull 模式,消息存放在服務端,只有消費者主動獲取才能拿到消息。消費者需要寫一個 while 循環不斷地從隊列獲取消息嗎?不需要,我們可以基於事件機制,實現消費者對隊列的監聽。
    由於隊列有 FIFO 的特性,只有確定前一條消息被消費者接收之後,纔會把這條消息從數據庫刪除,繼續投遞下一條消息。
  5. Exchange
    在 RabbitMQ 裏面永遠不會出現消息直接發送到隊列的情況。因爲在 AMQP 裏面引入了交換機(Exchange)的概念,用來實現消息的靈活路由。
    交換機是一個綁定列表,用來查找匹配的綁定關係。隊列使用綁定鍵(Binding Key)跟交換機建立綁定關係。生產者發送的消息需要攜帶路由鍵(Routing Key),交換機收到消息時會根據它保存的綁定列表,決定將消息路由到哪些與它綁定的隊列上。注意:交換機與隊列、隊列與消費者都是多對多的關係。
  6. Vhost
    我們每個需要實現基於 RabbitMQ 的異步通信的系統,都需要在服務器上創建自己要用到的交換機、隊列和它們的綁定關係。如果某個業務系統不想跟別人混用一個系統,怎麼辦?再採購一臺硬件服務器單獨安裝一個 RabbitMQ 服務?這種方式成本太高了。在同一個硬件服務器上安裝多個 RabbitMQ 的服務呢?比如再運行一個 5673 的端口?
    沒有必要,因爲 RabbitMQ 提供了虛擬主機 VHOST。VHOST 除了可以提高硬件資源的利用率之外,還可以實現資源的隔離和權限的控制。它的作用類似於編程語言中的 namespace 和 package,不同的 VHOST 中可以有同名的 Exchange 和 Queue,它們是完全透明的。
    這個時候,我們可以爲不同的業務系統創建不同的用戶(User),然後給這些用戶分配 VHOST 的權限。比如給風控系統的用戶分配風控系統的 VHOST 的權限,這個用戶可以訪問裏面的交換機和隊列。給超級管理員分配所有 VHOST 的權限。
  7. Producer生產者:主要將消息投遞到對應的Exchange上面。一般是獨立的程序。
  8. Consumer消費者:消息的接收者,一般是獨立的程序。

1.5 三種主要的交換機

1.5.1 Direct Exchange 直連交換機

定義:直連類型的交換機與一個隊列綁定時,需要指定一個明確的binding key。
路由規則:發送消息到直連類型的交換機時,只有routing key跟binding key完全匹配時,綁定的隊列才能收到消息。
在這裏插入圖片描述

例如:

 // 只有隊列1能收到消息
channel.basicPublish("MY_DIRECT_EXCHANGE", "key1", null, msg.getBytes());

1.5.2 Topic Exchange 主題交換機

定義:主題類型的交換機與一個隊列綁定時,可以指定按模式匹配的routing key。
通配符有兩個,* 代表匹配一個單詞。# 代表匹配零個或者多個單詞。單詞與單詞之間用 . 隔開。
路由規則:發送消息到主題類型的交換機時,routing key符合binding key的模式時,綁定的隊列才能收到消息。
在這裏插入圖片描述
例如:

 // 只有隊列1能收到消息
channel.basicPublish("MY_TOPIC_EXCHANGE", "ab.123", null, msg.getBytes());
// 隊列2和隊列3能收到消息
channel.basicPublish("MY_TOPIC_EXCHANGE", "bc.abc", null, msg.getBytes());

1.5.2 Fanout Exchange 廣播交換機

定義:廣播類型的交換機與一個隊列綁定時,不需要指定binding key。
路由規則:當消息發送到廣播類型的交換機時,不需要指定routing key,所有與之綁定的隊列都能收到消息。
在這裏插入圖片描述
例如:

 // 3個隊列都會收到消息
channel.basicPublish("MY_FANOUT_EXCHANGE", "", null, msg.getBytes());

二、安裝運行

2.1 windows和linux系統

直接官網下載壓縮包,解壓即可,到sbin目錄啓動rabbitmq-server即可。

2.2 mac

mac系統下載安裝包也可以,我是用homebrew安裝的。

brew install rabbitmq

安裝後會有啓動命令。

# 啓動
$ brew services start rabbitmq

# 重啓
$ brew services restart rabbitmq

# 停止
$ brew services stop rabbitmq

啓動完成訪問http://localhost:15672/#/。看到下面頁面就是安裝成功了。
在這裏插入圖片描述
賬號密碼都是guest。也不用輸入。直接login就行。

三、簡單的生產消費例子

先引入pom依賴。基於springboot的。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

3.1 生產者

package com.example.demo;

/**
 * @author : pengweiwei
 * @date : 2020/1/29 7:09 下午
 */

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.util.Map;

public class MyProducer {
    private final static String QUEUE_NAME = "ORIGIN_QUEUE";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        // 連接IP
        factory.setHost("127.0.0.1");
        // 連接端口
        factory.setPort(5672);
        // 虛擬機
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 建立連接
        Connection conn = factory.newConnection();
        // 創建消息通道
        Channel channel = conn.createChannel();

        String msg = "Hello, RabbitMQ";
        // 聲明隊列
        /**
         * 第一個參數:String queue
         * 第二個參數:boolean durable
         * 第三個參數:boolean exclusive,
         * 第四個參數:boolean autoDelete
         * 第五個參數:Map<String, Object> arguments
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 發送消息(發送到默認交換機AMQP Default,Direct) 如果有一個隊列名稱跟Routing Key相等,那麼消息會路由到這個隊列

        /**
         * 第一個參數:String exchange
         * 第二個參數:String routingKey
         * 第三個參數:BasicProperties props
         * 第四個參數:byte[] body
         */
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        channel.close();
        conn.close();
    }
}

3.2 消費者

package com.example.demo;

/**
 * @author : pengweiwei
 * @date : 2020/1/29 7:09 下午
 */

import com.rabbitmq.client.*;

import java.io.IOException;

public class MyConsumer {
    private final static String QUEUE_NAME = "ORIGIN_QUEUE";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory(); 
        // 連接IP
        factory.setHost("127.0.0.1");
        // 默認監聽端口
        factory.setPort(5672);
        // 虛擬機
        factory.setVirtualHost("/");
        // 設置訪問的用戶
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 建立連接
        Connection conn = factory.newConnection();
        // 創建消息通道
        Channel channel = conn.createChannel();
        /**
         * 聲明隊列
         * 第一個參數:String queue
         * 第二個參數:boolean durable
         * 第三個參數:boolean exclusive,
         * 第四個參數:boolean autoDelete
         * 第五個參數:Map<String, Object> arguments
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println(" Waiting for message....");
        // 創建消費者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("Received message : '" + msg + "'");
            }
        };
        /**
         * 開始獲取消息
         * 第一個參數:String queue
         * 第二個參數:boolean autoAck
         * 第三個參數:Consumer callback
         */
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

2.3 運行結果

在這裏插入圖片描述

2.4 參數說明

聲明交換機的參數

String type:交換機的類型,direct, topic, fanout中的一種。
boolean durable:是否持久化,代表交換機在服務器重啓後是否還存在。

聲明隊列的參數

boolean durable:是否持久化,代表隊列在服務器重啓後是否還存在。
boolean exclusive:是否排他性隊列。排他性隊列只能在聲明它的Connection中使用,連接斷開時自動刪除。
boolean autoDelete:是否自動刪除。如果爲true,至少有一個消費者連接到這個隊列,之後所有與這個隊列連接 的消費者
都斷開時,隊列會自動刪除。
Map<String, Object> arguments:隊列的其他屬性。含義詳細見下表。
屬性 含義
x-message-ttl 隊列中消息的存活時間,單位毫秒
x-expires 隊列在多久沒有消費者訪問以後會被刪除
x-max-length 隊列的最大消息數
x-max-length-bytes 隊列的最大容量,單位 Byte
x-dead-letter-exchange 隊列的死信交換機
x-dead-letter-routing-key 死信交換機的路由鍵
x-max-priority 隊列中消息的最大優先級,消息的優先級不能超過它

消息屬性 BasicProperties
主要的參數:

參數 釋義
Map<String,Object> headers 消息的其他自定義參數
Integer deliveryMode 2表示持久化,其他:瞬態
Integer priority 消息的優先級
String correlationId 關聯 ID,方便 RPC 相應與請求關聯
String replyTo 回調隊列
String expiration TTL , 消息過期時間,單位毫秒
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章