林炳文Evankaka原創作品。轉載請註明出處http://blog.csdn.net/evankaka
摘要:本文介紹了rabbitMq,提供了如何在Ubuntu下安裝RabbitMQ 服務的方法。最好以RabbitMQ與java、Spring結合的兩個執行個體來示範如何使用RabbitMQ。
本文工程免費下載
一、rabbitMQ簡介
1.1、rabbitMQ的優點(適用範圍)
1. 基於erlang語言開發具有高可用高並發的優點,適合叢集伺服器。
2. 健壯、穩定、易用、跨平台、支援多種語言、文檔齊全。
3. 有訊息確認機制和持久化機制,可靠性高。
4. 開源
其他MQ的優勢:
1. Apache ActiveMQ曝光率最高,但是可能會丟訊息。
2. ZeroMQ延遲很低、支援靈活拓撲,但是不支援訊息持久化和崩潰恢複。
1.2、幾個概念說明
producer&Consumer
producer指的是訊息生產者,consumer訊息的消費者。
Queue
訊息佇列,提供了FIFO的處理機制,具有緩衝訊息的能力。rabbitmq中,隊列訊息可以設定為持久化,臨時或者自動刪除。
設定為持久化的隊列,queue中的訊息會在server本地硬碟儲存一份,防止系統crash,資料丟失
設定為暫存佇列,queue中的資料在系統重啟之後就會丟失
設定為自動刪除的隊列,當不存在使用者串連到server,隊列中的資料會被自動刪除Exchange
Exchange類似於資料通訊網路中的交換器,提供訊息路由策略。rabbitmq中,producer不是通過通道直接將訊息發送給queue,而是先發送給Exchange。一個Exchange可以和多個Queue進行綁定,producer在傳遞訊息的時候,會傳遞一個ROUTING_KEY,Exchange會根據這個ROUTING_KEY按照特定的路由演算法,將訊息路由給指定的queue。和Queue一樣,Exchange也可設定為持久化,臨時或者自動刪除。
Exchange有4種類型:direct(預設),fanout, topic, 和headers,不同類型的Exchange轉寄訊息的策略有所區別:
Direct
直接交換器,工作方式類似於單播,Exchange會將訊息發送完全符合ROUTING_KEY的Queue
fanout
廣播是式交換器,不管訊息的ROUTING_KEY設定為什麼,Exchange都會將訊息轉寄給所有綁定的Queue。
topic
主題交換器,工作方式類似於組播,Exchange會將訊息轉寄和ROUTING_KEY匹配模式相同的所有隊列,比如,ROUTING_KEY為user.stock的Message會轉寄給綁定匹配模式為 * .stock,user.stock, * . * 和#.user.stock.#的隊列。( * 表是匹配一個任意片語,#表示匹配0個或多個片語)
headers
訊息體的header匹配(ignore)
Binding
所謂綁定就是將一個特定的 Exchange 和一個特定的 Queue 綁定起來。Exchange 和Queue的綁定可以是多對多的關係。
virtual host
在rabbitmq server上可以建立多個虛擬message broker,又叫做virtual hosts (vhosts)。每一個vhost本質上是一個mini-rabbitmq server,分別管理各自的exchange,和bindings。vhost相當於物理的server,可以為不同app提供邊界隔離,使得應用安全的運行在不同的vhost執行個體上,相互之間不會干擾。producer和consumer串連rabbit server需要指定一個vhost。
1.3、訊息佇列的使用過程
1. 用戶端串連到訊息佇列伺服器,開啟一個channel。
2. 用戶端聲明一個exchange,並設定相關屬性。
3. 用戶端聲明一個queue,並設定相關屬性。
4. 用戶端使用routing key,在exchange和queue之間建立好綁定關係。
5. 用戶端投遞訊息到exchange。
6. exchange接收到訊息後,就根據訊息的key和已經設定的binding,進行訊息路由,將訊息投遞到一個或多個隊列裡
二、環境配置與安裝
1、Erlang環境安裝
RabbitMQ是基於Erlang的,所以首先必須配置Erlang環境。
從Erlang的官網 http://www.erlang.org/download.html 下載最新的erlang安裝包,我下載的版本是 otp_src_R14B03.tar.gz 。然後:
$ tar xvzf otp_src_R14B03.tar.gz$ cd otp_src_R14B03$ ./configure
編譯後的輸出
如下圖:
註:
可能會報錯 configure: error: No curses library functions found
configure: error: /bin/sh '/home/liyixiang/erlang/configure' failed for erts
原因是缺少ncurses包 解決:在ubuntu系統下
apt-cache search ncursesapt-get install libncurses5-dev
然後重新執行
./configure
提示沒有wxWidgets和fop、ssh、odbc、ssl,但是問題不大。繼續:
make
然後:
sudo make install
配置erlang環境變數
修改/etc/profile檔案,增加下面的環境變數:(vim profile i插入 編輯完畢ESC退出 wq!強制修改)
#set erlang environmentexport PATH=$PATH:/usr/erlang/bin:$PATHsource profile使得檔案生效
下面是我的
2、RabbitMQ-Server安裝
安裝完Erlang,開始安裝RabbitMQ-Server。安裝方法有三種,這裡筆者三者都試過了,就只有以下這個方法成功了。
直接使用:
apt-get install rabbitmq-server
安裝完成後會自動開啟:
使用命令查看rabbitmq運行狀態:
rabbitmqctl status
停止
rabbitmqctl stop
開啟
rabbitmq-server start
3、rabbitmq web管理頁面外掛程式安裝
輸入以下命令
cd /usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management
這裡筆者一直安裝不成功。
如果安裝成功開啟瀏覽器,輸入 http://[server-name]:15672/ 如 http://localhost:15672/ ,會要求輸入使用者名稱和密碼,用預設的guest/guest即可(guest/guest使用者只能從localhost地址登入,如果要配置遠程登入,必須另建立使用者)。
如果要從遠程登入怎麼做呢。處於安全考慮,guest這個預設的使用者只能通過http://localhost:15672來登入,其他的IP無法直接用這個guest帳號。這裡我們可以通過設定檔來實現從遠程登入管理介面,只要編輯/etc/rabbitmq/rabbitmq.config檔案(沒有就新增),添加以下配置就可以了。
4、添加使用者
vim /etc/rabbitmq/rabbitmq.config
然後添加
[ {rabbit, [{tcp_listeners, [5672]}, {loopback_users, ["asdf"]}]} ]. 注意上面有個點號
現在添加了一個新授權使用者asdf,可以遠程使用這個使用者名稱。記得要先用命令添加這個命令才行:
cd /usr/lib/rabbitmq/bin/
#使用者名稱與密碼
sudo rabbitmqctl add_user asdf 123456
使用者佈建為administrator才能遠端存取
sudo rabbitmqctl set_user_tags asdf administrator sudo rabbitmqctl set_permissions -p / asdf ".*" ".*" ".*"
其實也可以通過管理平台頁面直接添加使用者和密碼等資訊。如果還不能遠端存取或遠程登入檢查是不是5672, 15672連接埠沒有開放。。。。。。
5、開放連接埠
ufw allow 5672
三、簡單Java執行個體 下面來示範一個使用java的簡單一實例: 1、首先是訊息生產者和提供者的基類
package com.lin;import java.io.IOException;import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory; /** * * 功能概要: EndPoint類型的隊列 * * @author linbingwen * @since 2016年1月11日 */public abstract class EndPoint{ protected Channel channel; protected Connection connection; protected String endPointName; public EndPoint(String endpointName) throws IOException{ this.endPointName = endpointName; //Create a connection factory ConnectionFactory factory = new ConnectionFactory(); //hostname of your rabbitmq server factory.setHost("10.75.4.25"); factory.setPort(5672); factory.setUsername("asdf"); factory.setPassword("123456"); //getting a connection connection = factory.newConnection(); //creating a channel channel = connection.createChannel(); //declaring a queue for this channel. If queue does not exist, //it will be created on the server. channel.queueDeclare(endpointName, false, false, false, null); } /** * 關閉channel和connection。並非必須,因為隱含是自動調用的。 * @throws IOException */ public void close() throws IOException{ this.channel.close(); this.connection.close(); }}
2、訊息提供者
package com.lin.producer;import java.io.IOException;import java.io.Serializable;import org.apache.commons.lang.SerializationUtils;import com.lin.EndPoint; /** * * 功能概要:訊息生產者 * * @author linbingwen * @since 2016年1月11日 */public class Producer extends EndPoint{ public Producer(String endPointName) throws IOException{ super(endPointName); } public void sendMessage(Serializable object) throws IOException { channel.basicPublish("",endPointName, null, SerializationUtils.serialize(object)); } }
3、訊息消費者
package com.lin.consumer;import java.io.IOException;import java.util.HashMap;import java.util.Map;import org.apache.commons.lang.SerializationUtils;import com.lin.EndPoint;import com.rabbitmq.client.AMQP.BasicProperties;import com.rabbitmq.client.Consumer;import com.rabbitmq.client.Envelope;import com.rabbitmq.client.ShutdownSignalException; /** * * 功能概要:讀取隊列的程式端,實現了Runnable介面 * * @author linbingwen * @since 2016年1月11日 */public class QueueConsumer extends EndPoint implements Runnable, Consumer{ public QueueConsumer(String endPointName) throws IOException{ super(endPointName); } public void run() { try { //start consuming messages. Auto acknowledge messages. channel.basicConsume(endPointName, true,this); } catch (IOException e) { e.printStackTrace(); } } /** * Called when consumer is registered. */ public void handleConsumeOk(String consumerTag) { System.out.println("Consumer "+consumerTag +" registered"); } /** * Called when new message is available. */ public void handleDelivery(String consumerTag, Envelope env, BasicProperties props, byte[] body) throws IOException { Map map = (HashMap)SerializationUtils.deserialize(body); System.out.println("Message Number "+ map.get("message number") + " received."); } public void handleCancel(String consumerTag) {} public void handleCancelOk(String consumerTag) {} public void handleRecoverOk(String consumerTag) {} public void handleShutdownSignal(String consumerTag, ShutdownSignalException arg1) {}}
4、測試
package com.lin.test;import java.io.IOException;import java.sql.SQLException;import java.util.HashMap;import com.lin.consumer.QueueConsumer;import com.lin.producer.Producer; public class Test { public Test() throws Exception{ QueueConsumer consumer = new QueueConsumer("queue"); Thread consumerThread = new Thread(consumer); consumerThread.start(); Producer producer = new Producer("queue"); for (int i = 0; i < 1000000; i++) { HashMap message = new HashMap(); message.put("message number", i); producer.sendMessage(message); System.out.println("Message Number "+ i +" sent."); } } /** * @param args * @throws SQLException * @throws IOException */ public static void main(String[] args) throws Exception{ new Test(); }} 其中引入的jar包:
<!-- rabbitmq用戶端 --><dependencies><dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>3.0.4</version></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.1</version></dependency></dependencies>
測試結果: 在提供訊息 在消費訊息
然後同時開啟rabbitmq的服務端,輸入如下: