一. RabbitMQ基礎知識
1.1 什麼是RabbitMQ
是一個開源的消息代理和隊列服務器,用來通過普通協議在完全不同的應用之間共享數據,RabbitMQ是使用Erlang語言來編寫的,並且RabbitMQ是基於AMQP協議的。
1.2 AMQP協議
是具有現代特徵的二進制協議,是一個提供統一消息服務的應用層標準高級消息隊列協議,是應用層協議的一個開放標準,爲面向消息中間件設計。
- Server:又稱Broker,接受客戶端的鏈接,實現AMQP的實體服務。
- Connection:鏈接,應用程序與Broker的網絡連接。
- Channel:網絡信道,幾乎所有的操作的在Channel中進行,Channel是進行消息讀寫的通道。客戶端可建立多個Channel,每個Channel代表以一個會話任務。
- Message: 消息,服務器和應用程序之間傳送的數據,由Properties和Bady組成,Properties可以對消息進行修飾,比如消息的優先級,延遲等高級特性;Body則就是消息體內容。
- Publisher application和Consumer application相當於是兩個系統
- Virtual host: 虛擬地址,用於進行邏輯隔離,最上層的消息路由。一個Virtual host裏面可以由若干個exchange和queue,同一個Virtual host不能由同名的exchange和queue
- Exchange: 交換機,接收消息的,根據路由健轉發消息到綁定的隊列。
- Binding:Exchange和queue之間的虛擬連接,binding中包含routing key
- routing key:一個路由規則,虛擬機可用它來確定如何路由一個特定消息。
- Message Queue 消息隊列,消息存放的路徑,建議與Exchange是一對多的關係,一個exchange可以對應多個queue。
生產者生產消息流程
生產者將消息投遞到X這個exchange上,exchange通過某種關係把消息路由到隊列上,消費者通過監聽隊列,獲取消息進行處理,一個隊列可以跟多個交換機exchange,但是不建議這麼做,因爲這麼做會提高代碼的複雜度,建議一個隊列對應一個交換機,同理一個消費者可以消費多個隊列的消息,但是建議一個消費者就消費一個隊列裏的消息。
消費者消費流程
生產者產生一條消息,通過exchange路由到某個隊列中,消費者通過監聽隊列,取消息進行消費。
二. RabbitMQ安裝
- 首先在Linux上進行一些軟件的準備工作,yum下來一些基礎的軟件包
yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz
- 下載RabbitMQ所需軟件包(這裏使用的是 RabbitMQ3.6.5 穩定版本,注意網站有時效性,如果下載不下來,可以評論告訴我,我給你發安裝包)
wget www.rabbitmq.com/releases/erlang/erlang-18.3-1.el7.centos.x86_64.rpm
wget http://repo.iotti.biz/CentOS/7/x86_64/socat-1.7.3.2-1.1.el7.lux.x86_64.rpm
wget www.rabbitmq.com/releases/rabbitmq-server/v3.6.5/rabbitmq-server-3.6.5-1.noarch.rpm
- 安裝服務命令
rpm -ivh erlang-18.3-1.el7.centos.x86_64.rpm
rpm -ivh socat-1.7.3.2-1.1.el7.x86_64.rpm
rpm -ivh rabbitmq-server-3.6.5-1.noarch.rpm
- 修改用戶登錄與連接心跳檢測,注意修改
vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app
修改點1:loopback_users 中的 <<“guest”>>,只保留guest (用於用戶登錄)
修改點2:heartbeat 爲10(用於心跳連接) - 首先啓動服務(後面 | 包含了停止、查看狀態以及重啓的命令)
/etc/init.d/rabbitmq-server start | stop | status | restart
- 查看服務有沒有啓動:
rabbitmq-plugins enable rabbitmq_management
- 可查看管理端口有沒有啓動: lsof -i:5672 (5672是Rabbit的默認端口)
lsof -i:15672 或者 netstat -tnlp | grep 15672
- 一切OK 我們訪問地址,輸入用戶名密碼均爲 guest :
http://你的ip地址:15672/
三. java集成RabbitMQ
- 引入pom文件,要和服務啓保持一致
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>
- 生產者代碼
package com.kingdee.rabbitmq.helloword;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
/**
* 生成者
*/
public class Sender {
public static void main(String[] args) throws Exception {
// 1 創建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.10.139");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
// 2 創建Connection
Connection connection = connectionFactory.newConnection();
// 3 創建Channel
Channel channel = connection.createChannel();
// 4 聲明
String queueName = "test001";
// 參數: queue名字,是否持久化,獨佔的queue(僅供此連接),不使用時是否自動刪除, 其他參數
channel.queueDeclare(queueName, false, false, false, null);
Map<String, Object> headers = new HashMap<String, Object>();
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
//是否持久化
.deliveryMode(2)
.contentEncoding("UTF-8")
.headers(headers).build();
for(int i = 0; i < 5;i++) {
String msg = "Hello World RabbitMQ " + i;
channel.basicPublish("", queueName , props , msg.getBytes());
}
}
}
- 消費者代碼
package com.kingdee.rabbitmq.helloword;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
/**
* 消費者
*/
public class Receiver {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("192.168.10.139");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String queueName = "test001";
// durable 是否持久化消息
channel.queueDeclare(queueName, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
// 參數:隊列名稱、是否自動ACK、Consumer
channel.basicConsume(queueName, true, consumer);
// 循環獲取消息
while(true){
// 獲取消息,如果沒有消息,這一步將會一直阻塞
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}
四. RabbitMQ核心api
4.1 Exchange
接收消息,並根據路由鍵轉發消息所綁定的隊列
4.1.1 交換機Exchange屬性
-
name:交換機名稱
-
Type:交換機類型
-
Direct Exchange – 處理路由鍵。需要將一個隊列綁定到交換機上,要求該消息與一個特定的路由鍵完全匹配。這是一個完整的匹配。如果一個隊列綁定到該交換機上要求路由鍵 “dog”,則只有被標記爲“dog”的消息才被轉發,不會轉發dog.puppy,也不會轉發dog.guard,只會轉發dog。
-
Topic Exchange – 將路由鍵和某模式進行匹配。此時隊列需要綁定要一個模式上。符號“#”匹配一個或多個詞,符號“”匹配不多不少一個詞。因此“audit.#”能夠匹配到“audit.irs.corporate”,但是“audit.” 只會匹配到“audit.irs”。
-
Fanout Exchange – 不處理路由鍵。你只需要簡單的將隊列綁定到交換機上。一個發送到交換機的消息都會被轉發到與該交換機綁定的所有隊列上。很像子網廣播,每臺子網內的主機都獲得了一份複製的消息。Fanout交換機轉發消息是最快的。
-
-
Durability: 是否需要持久化,True持久化
-
Auto Delete:當最後一個綁定到Exchange上的隊列刪除後,自動刪除該Exchange
-
Internal: 當前exchange是否用於RabbitMQ內部使用,默認爲false;
-
Arguments:擴展參數,用於擴展AMQP協議自制定化使用
參考資料:
https://www.cnblogs.com/ysocean/p/9251884.html