RabbitMQ入門教程

Fundebug發表於2018-05-09

摘要: 使用RabbitMQ的訊息佇列,可以有效提高系統的峰值處理能力。

RabbitMQ入門教程

RabbitMQ簡介

RabbitMQ訊息代理(Message Broker),它支援多種非同步訊息處理方式,最常見的有:

  • Work Queue:將訊息快取到一個佇列,預設情況下,多個worker按照Round Robin的方式處理佇列中的訊息。每個訊息只會分配給單個worker。
  • Publish/Subscribe:每個訂閱訊息的消費者都會收到訊息,因此每個訊息通常會分配給多個worker,每個worker對訊息進行不同的處理。

RabbitMQ還支援RoutingTopics、以及Remote procedure calls (RPC)等方式。

對於不同的訊息處理方式,有一點是相同的,RabbitMQ是介於訊息的生產者和消費者的中間節點,負責快取和分發訊息。RabbitMQ接收來自生產者的訊息,快取到記憶體中,按照不同的方式分發給消費者。RabbitMQ還可以將訊息寫入磁碟,保證持久化,這樣即使RabbitMQ意外崩潰了,訊息資料不至於完全丟失。

為什麼使用RabbitMQ?

最簡單的一點在於,它支援Work Queue等不同的訊息處理方式,可以用於不同的業務場景。對於我們Fundebug來說,目前只用過RabbitMQ的Work Queue,即訊息佇列。

使用訊息佇列,可以將不算緊急、但是非常消耗資源的計算任務,以訊息的方式插入到RabbitMQ的佇列中,然後使用多個處理模組處理這些訊息。

這樣做最大的好處在於:提高了系統峰值處理能力。因為,來不及處理的訊息快取在RabbitMQ中,避免了同時進行大量計算導致系統因超負荷執行而崩潰。而那些來不及處理的訊息,會在峰值過去之後慢慢處理掉。

另一個好處在於解耦。訊息的生產者只需要將訊息傳送給RabbitMQ,這些訊息什麼時候處理完,不會影響生產者的響應效能。

廣告:歡迎免費試用Fundebug,為您監控線上程式碼的BUG,提高使用者體驗~

安裝並執行RabbitMQ

使用Docker執行RabbitMQ非常簡單,只需要執行一條簡單的命令:

sudo docker run -d --name rabbitmq -h rabbitmq -p 5672:5672 -v /var/lib/rabbitmq:/var/lib/rabbitmq registry.docker-cn.com/library/rabbitmq:3.7
複製程式碼

對於不熟悉Docker的朋友,我解釋一下docker的命令選項:

  • -d : 後臺執行容器
  • --name rabbitmq : 將容器的名字設為rabbitmq
  • -h rabbitmq : 將容器的主機名設為rabbitmq,希望RabbitMQ訊息資料持久化儲存到本地磁碟是需要設定主機名,因為RabbitMQ儲存資料的目錄為主機名
  • -p 5672:5672 : 將容器的5672埠對映為本地主機的5672埠,這樣可以通過本地的5672埠訪問rabbitmq
  • -v /var/lib/rabbitmq:/var/lib/rabbitmq:將容器的/var/lib/rabbitmq目錄對映為本地主機的/var/lib/rabbitmq目錄,這樣可以將RabbitMQ訊息資料持久化儲存到本地磁碟,即使RabbitMQ容器被刪除,資料依然還在。

Docker為官方映象提供了加速服務,因此命令中Rabbit的Docker映象名為registry.docker-cn.com/library/rabbitmq:3.7

如果你不會Docker,建議你學習一下。如果你不想學,Ubuntu 14.04下安裝RabbitMQ的命令是這樣的:

sudo echo "deb http://www.rabbitmq.com/debian testing main" | sudo tee -a /etc/apt/sources.list
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install rabbitmq-server
複製程式碼

啟動RabbitMQ:

sudo service rabbitmq-server start
複製程式碼

訊息佇列程式碼示例

下面,我們使用Node.js實現一個簡單訊息佇列。

RabbitMQ入門教程

訊息的生產者:sender.js

const amqp = require("amqplib");

const queue = "demo";

async function sendMessage(message)
{
    const connection = await amqp.connect("amqp://localhost");
    const channel = await connection.createChannel();
    await channel.assertQueue(queue);
    await channel.sendToQueue(queue, new Buffer(message),
    {
        // RabbitMQ關閉時,訊息會被儲存到磁碟
        persistent: true
    });
}


setInterval(function()
{
    sendMessage("Hello, Fundebug!");
}, 1000)
複製程式碼
  • 在sender中,不斷地往訊息佇列中傳送"Hello, Fundebug!"。

訊息的消費者:receiver.js

const amqp = require("amqplib");

const queue = "demo";

async function receiveMessage()
{
    const connection = await amqp.connect("amqp://localhost");
    const channel = await connection.createChannel();
    await channel.assertQueue(queue);
    await channel.consume(queue, function(message)
    {
        console.log(message.content.toString());
        channel.ack(message);
    });
}

receiveMessage();
複製程式碼
  • 在receiver中,從訊息佇列中讀出message並列印。

我們用到了amqplib模組,用於與RabbitMQ進行通訊,對於具體介面的細節,可以檢視文件

在呼叫sendToQueue時,將persistent屬性設為true,這樣RabbitMQ關閉時,訊息會被儲存到磁碟。測試這一點很簡單:

  • 關閉receiver
  • 啟動sender,傳送訊息給RabbitMQ
  • 重啟RabbitMQ(sudo docker restart rabbitmq)
  • 啟動receiver,會發現它可以接收sender在RabbitMQ重啟之前傳送的訊息

由於RabbitMQ容器將儲存資料的目錄(/var/lib/rabbitmq)以資料卷的形式儲存在本地主機,因此即使將RabbitMQ容器刪除(sudo docker rm -f rabbitmq)後重新執行,效果也是一樣的。

另外,這段程式碼採用了Node.js最新的非同步程式碼編寫方式:Async/Await,因此非常簡潔,感興趣的同學可以瞭解一下。

這個Demo的執行方式非常簡單:

  • 執行RabbitMQ容器
sudo ./start_rabbitmq.sh
複製程式碼
  • 傳送訊息
node ./sender.js
複製程式碼
  • 接收訊息
node ./receiver.js
複製程式碼

在receiver端,可以看到不停地列印"Hello, Fundebug!"。

程式碼倉庫地址為:Fundebug/rabbitmq-demo

自動重連程式碼示例

在生產環境中,RabbitMQ難免會出現重啟的情況,比如更換磁碟或者伺服器、負載過高導致崩潰。因為RabbitMQ可以將訊息寫入磁碟,所以資料是"安全"的。但是,程式碼中必須實現自動重連機制,否則RabbitMQ停止時會導致Node.js應用崩潰。這裡提供一個自動重連的程式碼示例,給大家參考:

訊息生產者:sender_reconnect.js

const amqp = require("amqplib");

const queue = "demo";

var connection;

// 連線RabbitMQ
async function connectRabbitMQ()
{
    try
    {
        connection = await amqp.connect("amqp://localhost");
        console.info("connect to RabbitMQ success");

        const channel = await connection.createChannel();
        await channel.assertQueue(queue);
        await channel.sendToQueue(queue, new Buffer("Hello, Fundebug!"),
        {
            // RabbitMQ重啟時,訊息會被儲存到磁碟
            persistent: true
        });

        connection.on("error", function(err)
        {
            console.log(err);
            setTimeout(connectRabbitMQ, 10000);
        });

        connection.on("close", function()
        {
            console.error("connection to RabbitQM closed!");
            setTimeout(connectRabbitMQ, 10000);
        });

    }
    catch (err)
    {
        console.error(err);
        setTimeout(connectRabbitMQ, 10000);
    }
}


connectRabbitMQ();
複製程式碼

訊息消費者:receiver_reconnect.js

const amqp = require("amqplib");

const queue = "demo";

var connection;

// 連線RabbitMQ
async function connectRabbitMQ()
{
    try
    {
        connection = await amqp.connect("amqp://localhost");
        console.info("connect to RabbitMQ success");

        const channel = await connection.createChannel();
        await channel.assertQueue(queue);
        await channel.consume(queue, async function(message)
        {
            console.log(message.content.toString());
            channel.ack(message);
        });

        connection.on("error", function(err)
        {
            console.log(err);
            setTimeout(connectRabbitMQ, 10000);
        });

        connection.on("close", function()
        {
            console.error("connection to RabbitQM closed!");
            setTimeout(connectRabbitMQ, 10000);
        });

    }
    catch (err)
    {
        console.error(err);
        setTimeout(connectRabbitMQ, 10000);
    }
}


connectRabbitMQ();
複製程式碼

這樣的話,即使RabbitMQ重啟,sender和receiver也可以自動重新連線RabbitMQ。如果你希望監控RabbitMQ是否出錯,不妨使用我們Fundebug的Node.js錯誤監控服務,在連線觸發"error"或者"close"事件時,第一時間傳送報警,這樣開發者可以及時定位和處理BUG。

參考

關於Fundebug

Fundebug專注於JavaScript、微信小程式、微信小遊戲、支付寶小程式、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟體、百姓網等眾多品牌企業。歡迎大家免費試用

RabbitMQ入門教程

版權宣告

轉載時請註明作者Fundebug以及本文地址:
blog.fundebug.com/2018/04/20/…

相關文章