[Open Source] RabbitMQ 安裝與使用

Never、C發表於2017-03-19

前言

吃多了拉就是佇列,吃飽了吐就是棧

  • 使用場景
    • 對操作的實時性要求不高,而需要執行的任務極為耗時;(傳送簡訊,郵件提醒,更新文章閱讀計數,記錄使用者操作日誌)
    • 存在異構系統間的整合;

安裝

  • 下載 Erlang

    • 安裝完確定ERLANG_HOME環境變數是否新增,否則:Setx ERLANG_HOME “D:\Program Files\erl8.2″
  • 下載安裝包

    • 安裝完透過rabbitmqctl status確定rabbitmq狀態
  • 管理服務

    • 預設安裝成功會自動啟動服務
    • 透過開始選單可以啟動,停止,解除安裝服務
  • 佔用埠

    • 4369(叢集、Erlang)
    • 5671,5672(應用層標準高階訊息佇列協議)
    • 25672(Erlang分發,CLI通訊)
    • 15672(如果管理外掛啟用)
    • 61613,61614(如果訊息文字協議STOMP已啟用)
    • 1883,8883(如果erl實時通訊已啟用)
  • 支援的平臺

    • 基於Ubuntu和Debian的Linux發行版
    • 基於Fedora,CentOS和RPM的Linux發行版
    • Mac OS X
    • Windows XP及更高版本

概念

  • Connections:客戶端連線,建立該資源非常耗時,應儘量避免多次建立。

  • Channel:訊息通道,在客戶端的每個連線裡,可建立多個channel,每個channel代表一個會話任務。

  • Exchange:訊息交換機,它指定訊息按什麼規則,路由到哪個佇列。

  • Queue:訊息佇列載體,每個訊息都會被投入到一個或多個佇列。

  • Broker:簡單來說就是訊息佇列伺服器實體。

  • Binding:繫結,它的作用就是把exchange和queue按照路由規則繫結起來。

  • Routing Key:路由關鍵字,exchange根據這個關鍵字進行訊息投遞。

  • vhost:虛擬主機,一個broker裡可以開設多個vhost,用作不同使用者的許可權分離。

  • producer:訊息生產者,就是投遞訊息的程式。

  • consumer:訊息消費者,就是接收訊息的程式。

訊息佇列的傳送過程大概如下:

  1. 客戶端建立Connection,連線到訊息佇列伺服器,開啟一個channel。
  2. 客戶端宣告一個Exchange,並設定相關屬性。
  3. 客戶端宣告一個Queue,並設定相關屬性。
  4. 客戶端使用routing key,在exchange和queue之間建立好繫結關係。
  5. 客戶端傳送訊息首先到exchange
  6. exchange根據type路由到對應的佇列(可以是多個佇列)中.

Exchange Type

  • direct(直連)
    • routing key 與 binding key相同
  • fanout
    • 給所有繫結佇列傳送訊息
  • topic
    • routing key:audit.irs.corporate => binding key:audit.#
    • routing key:audit.irs => binding key:audit.*
  • default
    • direct
    • binding key為queue名稱

常用命令

  • 管理外掛

    • rabbitmq-plugins enable rabbitmq_management // 啟用
    • rabbitmq-plugins disable rabbitmq_management // 禁用
  • 管理佇列

    • rabbitmqctl list_queues // 檢視佇列
  • 管理使用者及許可權

    • rabbitmqctl list_users // 檢視所有使用者
    • rabbitmqctl add_user user_admin passwd_admin // 新增使用者
    • rabbitmqctl set_user_tags user_admin administrator // 新增許可權
    • rabbitmqctl delete_user guest // 刪除使用者
    • rabbitmqctl change_password {username} {newpassowrd} // 修改密碼
  • 管理虛擬主機vhost

    • rabbitmqctl add_vhost vhostpath // 建立虛擬主機
    • rabbitmqctl delete_vhost vhostpath // 刪除虛擬主機
    • rabbitmqctl list_vhosts // 列出所有虛擬主機

使用

  • 傳送訊息(以持久化程式碼為例)
var factory = new ConnectionFactory
{
    HostName = hostName,                // rabbit server
    UserName = "admin",
    Password = "admin",
    Port = 5672,                        // Broker埠
    VirtualHost = "/"                   // 虛擬Host,需提前配置
};

using (var connection = factory.CreateConnection()) // 建立與RabbitMQ伺服器的連線
{
    using (var channel = connection.CreateModel())  // 建立1個Channel(大部分API在該Channel中)
    {
        // 定義1個佇列,自動會和預設的exchange 做direct型別繫結
        channel.QueueDeclare(
            queue: "hello",                     // 佇列名稱
            durable: true,                      // 佇列是否持久化
            exclusive: false,                   // 排他佇列:如果一個佇列被宣告為排他佇列,該佇列僅對首次宣告它的連線可見,並在連線斷開時自動刪除。(活動在一次連線內)
            autoDelete: false,                  // 自動刪除:當最後一個消費者取消訂閱時,佇列自動刪除。如果您需要僅由一個使用者使用的臨時佇列,請將自動刪除與排除。當消費者斷​​開連線時,佇列將被刪除。(至少消費者能連一次)
            arguments: null);                   // 配置引數

        var randomQueue = channel.QueueDeclare();                   // 定義隨機的佇列 該佇列為臨時佇列(排他佇列 + 自動刪除)


        // 定義Exchange(一般而言,不需要定義exchange,rabbitmq預設建立了所有型別的exchange)
        //channel.ExchangeDeclare("direct-demo", ExchangeType.Direct);    // 定義direct exchange
        //channel.ExchangeDeclare("fannout-demo", ExchangeType.Fanout);   // 定義fanout exchange
        //channel.ExchangeDeclare("topic-demo", ExchangeType.Topic);      // 定義fanout exchange


        // 定義queue exchange key 關係(在某些業務場景下,會使用該關係做路由功能)
        //channel.QueueBind(queue: "hello", exchange: "amq.direct", routingKey: "hello"); // 預設繫結的關係和該行程式碼效果一樣
        //channel.QueueBind("hello", "amq.fanout", "hello");                              // 該型別下的routingKey 實際不需要

        var properties = channel.CreateBasicProperties();
        properties.Persistent = true;

        while (true)
        {
            string message = "Hello World!" + DateTime.Now;
            var body = Encoding.UTF8.GetBytes(message);

            // 傳送訊息到佇列中
            channel.BasicPublish(
                exchange: string.Empty,         // 傳遞為Empty的時候,透過	`(AMQP default)`傳遞
                routingKey: "hello",            // routing key 與 queuebind中的binding key對應
                basicProperties: properties,    // 訊息header
                body: body);                    // 訊息body:傳送的是bytes 可以任意編碼

            Console.WriteLine(" [x] Sent {0}", message);
        }
    }
}
  • 接收訊息(以訊息響應為例)
var factory = new ConnectionFactory
{
    HostName = hostName,                // rabbit server
    UserName = "admin",
    Password = "admin",
    Port = 5672,                        // Broker埠
    VirtualHost = "/"                   // 虛擬Host,需提前配置
};
using (var connection = factory.CreateConnection())
{
    using (var channel = connection.CreateModel())
    {
        var consumer = new EventingBasicConsumer(channel);  // 建立Consumer
        consumer.Received += (model, ea) =>         // 透過回撥函式非同步推送我們的訊息
        {
            var body = ea.Body;
            var message = Encoding.UTF8.GetString(body);
            Thread.Sleep(1000);
            channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); // 訊息響應
            Console.WriteLine(" [x] Received {0}", message);
        };

        channel.BasicQos(0, 1, false);  // 設定perfetchCount=1 。這樣就告訴RabbitMQ 不要在同一時間給一個工作者傳送多於1個的訊息
        channel.BasicConsume(queue: "hello",
                                noAck: false,      // 需要訊息響應(Acknowledgments)機制
                                consumer: consumer);

        Console.WriteLine(" Press [enter] to exit.");
        Console.ReadLine();
    }
}
  • 訊息響應(acknowledgments)

    • 為了防止訊息丟失,RabbitMQ提供了訊息響應(acknowledgments)機制。消費者會透過一個ack(響應),告訴RabbitMQ已經收到並處理了某條訊息,然後RabbitMQ才會釋放並刪除這條訊息。
  • 持久化

    • 新佇列(無法修改佇列)配置為可持久化
    • 傳送訊息配置為持久化
    • 訊息什麼時候刷到磁碟?
      • 寫入檔案前會有一個Buffer,大小為1M,資料在寫入檔案時,首先會寫入到這個Buffer,如果Buffer已滿,則會將Buffer寫入到檔案(未必刷到磁碟)。
      • 固定的刷盤時間:25ms,也就是不管Buffer滿不滿,每個25ms,Buffer裡的資料及未重新整理到磁碟的檔案內容必定會刷到磁碟。
      • 每次訊息寫入後,如果沒有後續寫入請求,則會直接將已寫入的訊息刷到磁碟:使用Erlang的receive x after 0實現,只要程式的信箱裡沒有訊息,則產生一個timeout訊息,而timeout會觸發刷盤操作。

常見問題

  • RabbitMQ 管理外掛啟動報錯

    • 確認RabbitMQ服務是否啟動
    • C:\Windows目錄下,將.erlang.cookie檔案,複製到使用者目錄下 C:\Users{使用者名稱},這是Erlang的Cookie檔案,允許與Erlang進行互動
    • 重新安裝erl 和 rabbit,儘量不要帶空格的路徑
  • 修改配置檔案

相關文章