RabbitMQ是一個消息代理:它接受和轉發消息。你可以把它想象成一個郵局:當你把你想寄出的郵件放進一個郵箱裏時,你可以確信郵件的收件人最終會收到郵件。類似的,RabbitMQ是一個郵箱、一個郵局和一個郵遞員。
RabbitMQ與郵局的主要區別在於,它不處理紙張信件,而是接受、存儲和轉發二進制的數據信息塊。
RabbitMQ和消息傳遞通常使用一些行業術語
-
生產只意味着發送。發送消息的程序是生產者:
-
queue是RabbitMQ中的郵箱的名稱。儘管消息流經RabbitMQ和您的應用程序,但它們只能存儲在隊列中。隊列只受主機的內存和磁盤限制的約束,它本質上是一個大的消息緩衝區。許多生產者可以將消息發送到一個隊列,而許多消費者可以嘗試從一個隊列接收數據。如下是我們表示隊列的方式
-
消費和接收有着相似的含義。消費者是一個主要等待接收消息的程序:
請注意,生產者、消費者和代理不必駐留在同一主機上;事實上,在大多數應用程序中,它們不必駐留在同一主機上。應用程序也可以既是生產者又是消費者。
“Hello World”
在這部分中,我們將用Java編寫兩個程序:一個是發送單個消息的生產者,另一個是接收消息並將其打印出來的消費者。我們將略過Java API中的一些細節,重點放在這件非常簡單的事情上,以便於快速開始上手。一個“Hello World”消息。
在下圖中,“P”是我們的生產者,“C”是我們的消費者。中間的框是一個隊列,是RabbitMQ持有的供消費者使用的的消息緩衝區。
Java客戶端庫
RabbitMQ有多種協議。本教程使用AMQP 0-9-1,這是一個開放的、通用的消息傳遞協議。RabbitMQ有許多不同語言的客戶機。我們將使用RabbitMQ提供的Java客戶端。下載客戶端庫及其依賴項(SLF4J API和SLF4J Simple)。將這些文件與教程中的Java文件一起復制到工作目錄中。
請注意,SLF4J Simple對於教程來說已經足夠了,但是您應該在生產中使用完整的日誌庫,比如Logback。
(RabbitMQ Java客戶端也在中央Maven存儲庫中,groupId爲com.rabbitmq,artifactId爲amqp-client.)
現在我們有了Java客戶端及其依賴項,我們可以編寫一些代碼。
發送
我們將調用我們的消息發佈者(sender)發送和消息使用者(receiver)接收。發佈者將連接到RabbitMQ,發送一條消息,然後退出。
在 Send.java,我們需要導入一些類:
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
設置啓動類並命名隊列:
public class Send {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
...
}
}
然後我們可以創建到服務器的連接:
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
}
該連接抽象了socket連接,爲我們負責協議版本協商和認證等工作。在這裏,我們連接到本地機器上的RabbitMQ節點-因此是本地主機。如果我們想連接到另一臺機器上的節點,我們只需在這裏指定它的主機名或IP地址。
接下來,我們創建一個通道,這是大多數完成任務的API所在的位置。注意,我們可以使用try-with-resources語句,因爲連接Connection和通道Channel都實現了java.io.Closeable. 這樣我們就不需要在代碼中顯式地關閉它們。
要發送,我們必須聲明要發送到的隊列;然後我們可以將消息發佈到隊列,所有這些都在try with resources語句中:
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
聲明隊列是等冪的——只有當它不存在時纔會創建它。消息內容是一個字節數組,因此您可以在那裏對任何您喜歡的內容進行編碼。
發送不起作用!
如果這是您第一次使用RabbitMQ,而您沒有看到已發送的消息,那麼您可能會撓頭想知道可能是什麼問題。可能代理啓動時沒有足夠的可用磁盤空間(默認情況下至少需要200 MB的可用空間),因此拒絕接受消息。檢查代理日誌文件以確認並在必要時降低限制。配置文件文檔 將向您展示如何設置磁盤可用空間限制。
接收
這是我們的發佈者。我們的消費者監聽來自RabbitMQ的消息,因此與發佈單個消息的發佈者不同,我們將保持它的運行以監聽消息並將其打印出來。
代碼(在 Recv.java 中)與Send有幾乎相同的import:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
我們將使用額外的DeliveryCallback接口來緩衝服務器推送到我們的消息。
設置與發佈服務器相同;我們打開一個連接和一個通道,並聲明要使用的隊列。注意,這與send發佈消息的隊列是匹配的。
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
}
}
注意,我們在這裏聲明瞭隊列。可能在生產者之前啓動了消費者,所以在嘗試使用隊列中的消息之前,我們要確保隊列存在。
爲什麼不使用try-with-resource語句自動關閉通道和連接?這樣做,可以讓程序運行,然後關閉所有內容,然後退出!這可能會很不方便,因爲我們希望進程在使用者異步偵聽消息到達時保持活動狀態。
我們將告訴服務器從隊列中向我們傳遞消息。由於它將異步地推送消息,因此我們以對象的形式提供回調,該回調將緩衝消息,直到我們準備好使用它們。這就是DeliverCallback子類所做的。
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
整合
您可以使用類路徑上的RabbitMQ java客戶端編譯這兩個文件:
javac -cp amqp-client-5.7.1.jar Send.java Recv.java
要運行它們,您需要rabbitmq-client.jar以及它對類路徑的依賴關係。在終端中,運行消費者(receiver):
java -cp .:amqp-client-5.7.1.jar:slf4j-api-1.7.26.jar:slf4j-simple-1.7.26.jar Recv
然後,運行生產者(sender):
java -cp .:amqp-client-5.7.1.jar:slf4j-api-1.7.26.jar:slf4j-simple-1.7.26.jar Send
在Windows上,使用分號而不是冒號分隔類路徑中的項。
消費者將通過RabbitMQ打印從生產者處獲得的消息。消費者將繼續運行,等待消息(使用Ctrl-C停止),因此嘗試從另一個終端運行發佈服務器。
列出隊列
您可能希望看到RabbitMQ有哪些隊列以及其中有多少消息。您可以使用rabbitmqctl工具(作爲特權用戶)執行此操作:sudo rabbitmqctl list_queues
在Windows上,省略sudo:
rabbitmqctl.bat list_queues
是時候進入第2部分並構建一個簡單的工作隊列了。
提示
爲了節省輸入,您可以爲類路徑設置一個環境變量,例如。
export CP=.:amqp-client-5.7.1.jar:slf4j-api-1.7.26.jar:slf4j-simple-1.7.26.jar
java -cp $CP Send
在Windows上:
set CP=.;amqp-client-5.7.1.jar;slf4j-api-1.7.26.jar;slf4j-simple-1.7.26.jar
java -cp %CP% Send