文章目錄
什麼是RabbitMQ
RabbitMQ是一個由ErLang語言編寫的實現了AMQP(高級消息隊列協議)的開源消息中間件 。RabbitMQ 支持消息的持久化、事務、擁塞控制、負載均衡等特性。
爲什麼要使用RabbitMQ
在實際開發過程中,我們可能會經常遇到一下情況
1、A系統需要調用B系統,比如發短信,發郵件
2、比如A系統需要同步數據到B系統
按照以往的方式,我們是直接在代碼裏面添加一個調用其它系統的方法。也許我們會使用異步的方式來調用它。但是不管怎樣,這個調用的過程還是發生在我們系統內部,一旦這個過程崩潰,將會導致我們的主業務功能不可用。
因此我們引入了消息中間件,而RabbitMQ就是其中的一種,引入消息中間件爲我們帶來一些幾點好處:
- 降低耦合度
把多個系統之間的調用放到了纖細中間件上面,降低了系統的耦合度 - 異步化
各個系統之間無需等待即可執行 - 提高系統吞吐能力
可以把高峯期的請求裏面耗時到操作緩衝到消息隊列裏面,提高請求的響應速度
AMQP協議
什麼是AMQP協議
AMQP是應用層協議的一個開放標準,爲面向消息的中間件設計。基於此協議的客戶端與消息中間件可傳遞消息,並不受客戶端/中間件不同產品,不同的開
發語言等條件的限制。目標是實現一種在全行業廣泛使用的標準消息中間件技術,以便降低企業和系統集成的開銷,並且向大衆提供工業級的集成服務。
RabbitMQjava客戶端的使用
幾個基本概念
要使用客戶端連接RabbitMQ,那麼首先我們應該弄清楚一下幾個概念:
連接
作爲一個客戶端,如果要和RabbitMQ通信,那麼首先肯定得建立一條TCP連接,同時告訴RabbitMQ,我們是使用AMQP進行交流,那麼這時候就會創建一條AMQP的信道
信道
RabbitMQ使用了多路複用技術來進行通信。在上面的tcp連接創建好之後,RabbitMQ就會創建出信道進行通信,信道是基於在TCP連接上面的虛擬連接,一個TCP連接可以有多個信道,每個信道之間都是私有的,這樣就避免了多次創建TCP連接帶來的性能損耗。
生產者
生產者就是消息的發佈者,把消息發送到RabbitMQ
消費者
消費者通過訂閱指定的隊列來消費生產者發佈的某些消息
交換器,路由鍵,隊列
生產者所生產的消息將會發送到指定的交換器,然後交換器會根據消息的路由鍵進行匹配,發送給綁定了對應的路由鍵的隊列。所以他們之間存在以下的關係:
虛擬主機
虛擬消息服務器,vhost,本質上就是一個 mini 版的 mq 服務器,有自己的隊列、交換器和綁定,最重要的,自己的權限機制。Vhost 提供了邏輯上的
分離,可以將衆多客戶端進行區分,又可以避免隊列和交換器的命名衝突。Vhost 必須在連接時指定,rabbitmq 包含缺省 vhost:“/”,通過缺省用戶和
口令 guest 進行訪問。
rabbitmq 裏創建用戶,必須要被指派給至少一個 vhost,並且只能訪問被指派內的隊列、交換器和綁定。Vhost 必須通過 rabbitmq 的管理控制工具創
建。
交換器類型
- direct交換器
路由鍵完全匹配,消息被投遞到對應的隊列, direct 交換器是默認交換器。聲明一個隊列時,會自動綁定到默認交換器,並且以隊列名稱作爲路由鍵 - Fanout交換器
消息廣播到綁定的隊列,不管隊列綁定了什麼路由鍵,消息經過交換器,每個隊列都有一份。 - Topic交換器
通過使用*和#通配符進行處理,使來自不同源頭的消息到達同一個隊列,. 將路由鍵分爲了幾個標識符,*匹配 一 個,#匹配一個或多個。
簡單的消息生產和消費
生產者代碼(Direct)
/**
* @Title:
* @MethodName:
* @param
* @Return
* @Exception
* @Description:
* direct 交換器的路由鍵匹配模式是全匹配,也就是需要完全相等纔可以匹配,所以一般用隊列的名稱作爲路由鍵
* @author: jenkin
* @date: 2020-04-11 10:04
*/
@Test
public void sendByDirect(){
Connection connection = Common.getConnection();
try {
//創建一個信道,用於消息通信
Channel channel = connection.createChannel();
//聲明一個持久化的direct交換器
channel.exchangeDeclare( DIRECT_EXCHANGE,"direct",true);
//聲明一個隊列,這裏持久化參數爲true,如果不持久化,那麼一旦mq出故障重啓就會丟失隊列以及隊列裏面的數據
//這裏exclusive爲false,標識這個隊列可以被多個消費者消費
//autoDelete爲false,如果爲true的話當隊列裏面沒有消費者之後就會自動刪除,bug前提是要被消費過之後纔會去判斷
//arguments這是一個map,可以聲明隊列的過期時間,最大空間,長度,以及超時時間
channel.queueDeclare(DIRECT_QUEUE_1,true,false,false,null);
//綁定隊列和交換器的關係
//這裏把DIRECT_QUEUE_1隊列綁定到DIRECT_EXCHANGE 交換器上面,並且使用DIRECT_QUEUE_1作爲路由鍵發送消息
channel.queueBind(DIRECT_QUEUE_1,DIRECT_EXCHANGE,DIRECT_QUEUE_1);
//使用信道發送消息
//mandatory 這個標識如果爲true,那麼如果消息無法被路由的時候就會返調用basic.Return 把消息返回給生產者,
//可以添加一個ReturnListener ,在消息被返回之後會在這裏進行回調
// 反之直接丟棄
channel.basicPublish(DIRECT_EXCHANGE,DIRECT_QUEUE_1,false,null,"測試消息".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
消費者代碼
/**
* @Title:
* @MethodName:
* @param
* @Return
* @Exception
* @Description:
* 接受direct類型的消息
* @author: jenkin
* @date: 2020-04-11 11:05
*/
public void consumerDirect(){
Connection connection = Common.getConnection();
try {
Channel channel = connection.createChannel();
channel.basicConsume(DIRECT_QUEUE_1,true,new QueueingConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消費者收到消息"+new String(body, StandardCharsets.UTF_8));
}
});
} catch (IOException e) {
e.printStackTrace();
}
}