RabbitMq知識整理以及在java語言下的簡單例項
簡單介紹:
使用Erlang語言編寫,主用於在分散式系統中儲存轉發訊息,使用AMQP協議(Connection,Queue,Channel,Exchange,Binding等概念,下面在mq介紹中解釋),相比於redis而言支援事物保證訊息可靠傳播,效能高,叢集穩定等優勢,但是redis更加輕量級,高敏感性。
一、元件介紹:
ConnectionFactory、Connection、Channel都是RabbitMQ對外提供的API中最基本的物件。Connection是RabbitMQ的socket連結,它封裝了socket協議相關部分邏輯。ConnectionFactory為Connection的製造工廠。 Channel是我們與RabbitMQ打交道的最重要的一個介面,我們大部分的業務操作是在Channel這個介面中完成的,包括定義Queue、定義Exchange、繫結Queue與Exchange、釋出訊息等。
1.Queue
Queue(佇列)是RabbitMQ的內部物件,用於儲存訊息;
多個消費者可以訂閱同一個Queue,這時Queue中的訊息會被平均分攤給多個消費者進行處理,而不是每個消費者都收到所有的訊息並處理。
2.Exchange
生產者將訊息傳送到Exchange(交換器,下圖中的X),由Exchange將訊息路由到一個或多個Queue中(或者丟棄)。
3.routing key
生產者在將訊息傳送給Exchange的時候,一般會指定一個routing key,來指定這個訊息的路由規則,而這個routing key需要與Exchange Type及binding key聯合使用才能最終生效。 在Exchange Type與binding key固定的情況下(在正常使用時一般這些內容都是固定配置好的),我們的生產者就可以在傳送訊息給Exchange時,通過指定routing key來決定訊息流向哪裡。
4.Binding
RabbitMQ中通過Binding將Exchange與Queue關聯起來,這樣RabbitMQ就知道如何正確地將訊息路由到指定的Queue了。
在繫結(Binding)Exchange與Queue的同時,一般會指定一個binding key;消費者將訊息傳送給Exchange時,一般會指定一個routing key;當binding key與routing key相匹配時,訊息將會被路由到對應的Queue中。 在繫結多個Queue到同一個Exchange的時候,這些Binding允許使用相同的binding key。 binding key 並不是在所有情況下都生效,它依賴於Exchange Type,比如fanout型別的Exchange就會無視binding key,而是將訊息路由到所有繫結到該Exchange的Queue。
5.Exchange Types
exchange怎麼和佇列queue繫結呢,按照什麼規則呢:
RabbitMQ常用的Exchange Type有fanout、direct、topic:
5.1 fanout
fanout型別的Exchange路由規則非常簡單,它會把所有傳送到該Exchange的訊息路由到所有與它繫結的Queue中。
5.2 direct
direct型別的Exchange路由規則也很簡單,它會把訊息路由到那些binding key與routing key完全匹配的Queue中。
5.3 topic
前面講到direct型別的Exchange路由規則是完全匹配binding key與routing key,但這種嚴格的匹配方式在很多情況下不能滿足實際業務需求。topic型別的Exchange在匹配規則上進行了擴充套件,它與direct型別的Exchage相似,也是將訊息路由到binding key與routing key相匹配的Queue中,但這裡的匹配規則有些不同,它約定:
-
routing key為一個句點號“. ”分隔的字串
-
binding key與routing key一樣也是句點號“. ”分隔的字串
-
binding key中可以存在兩種特殊字元“*”與“#”,用於做模糊匹配,其中“*”用於匹配一個單詞,“#”用於匹配多個單詞(可以是零個)
二、生產訊息(confirm模式的程式設計實現):
RabbitMq類:
public class RabbitMq
{
public static final String exchangeName = "exchange";
public static final String queueName = "queue";
public static final String routingKey = "routingKey";
public static final String bindingKey = "routingKey";
public static void main(String[] args)
{
int count = 1;
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setUsername("guest");
factory.setPassword("guest");
factory.setPort(5672);
// 建立生產者
Sender producer = new Sender(factory, count, exchangeName, queueName, routingKey, bindingKey);
producer.run();
}
}
傳送訊息類:
public class Sender
{
private ConnectionFactory factory;
private int count;
private String exchangeName;
private String queueName;
private String routingKey;
private String bindingKey;
public Sender(ConnectionFactory factory, int count, String exchangeName, String queueName, String routingKey,
String bindingKey)
{
this.factory = factory;
this.count = count;
this.exchangeName = exchangeName;
this.queueName = queueName;
this.routingKey = routingKey;
this.bindingKey = bindingKey;
}
public void run()
{
Channel channel = null;
try
{
Connection connection = factory.newConnection();
channel = connection.createChannel();
// 建立exchange,採用direct方式完全匹配
channel.exchangeDeclare(exchangeName, "direct", true, false, null);
// 建立佇列,true為持久化訊息
channel.queueDeclare(queueName, true, false, false, null);
// 繫結exchange和queue
channel.queueBind(queueName, exchangeName, bindingKey);
channel.confirmSelect();
// 傳送持久化訊息
for (int i = 0; i < count; i++)
{
// 第一個引數是exchangeName(預設情況下代理伺服器端是存在一個""名字的exchange的,
// 因此如果不建立exchange的話我們可以直接將該引數設定成"",如果建立了exchange的話
// 我們需要將該引數設定成建立的exchange的名字),第二個引數是路由鍵
channel.basicPublish(exchangeName,
routingKey,
MessageProperties.PERSISTENT_BASIC,
("第" + (i + 1) + "條訊息").getBytes());
}
long start = System.currentTimeMillis();
channel.addConfirmListener(new ConfirmListener() //使用listener非同步確認效率高,還有序列confirm,批量序列效率較 低,程式設計簡單
{
@Override
public void handleNack(long deliveryTag, boolean multiple)
throws IOException
{
System.out.println("nack: deliveryTag = " + deliveryTag + " multiple: " + multiple);
}
@Override
public void handleAck(long deliveryTag, boolean multiple)
throws IOException
{
System.out.println("ack: deliveryTag = " + deliveryTag + " multiple: " + multiple);
}
});
System.out.println("執行ConfirmListener耗費時間: " + (System.currentTimeMillis() - start) + "ms");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
三,消費者程式碼:
public class ConsumerTest
{
/**
* 訂閱方式其實是向queue註冊consumer,通過rpc向queue server傳送註冊consumer的訊息,
* rabbitMQ Server在收到訊息後,根據訊息的內容型別判斷這是一個訂閱訊息,
這樣當MQ 中queue有訊息時,會自動把訊息通過該socket(長連線)通道傳送出去。
*/
public static void main(String[] args) throws IOException, ShutdownSignalException,
ConsumerCancelledException, InterruptedException, TimeoutException {
// 建立連結工廠
ConnectionFactory connFac = new ConnectionFactory() ;
//預設連結的主機名,RabbitMQ-Server安裝在本機,所以可以直接用127.0.0.1
connFac.setHost("127.0.0.1");
//建立連結
Connection conn = connFac.newConnection() ;
//建立資訊管道
final Channel channel = conn.createChannel() ;
//定義Queue名稱
String queueName = RabbitMq.queueName;
//1.佇列名2.是否持久化,3是否侷限與連結,4不再使用是否刪除,5其他的屬性 是否持久化需要和生成者佇列設定的一樣否則報錯
channel.queueDeclare(queueName, true, false, false, null) ;
// 同一時刻伺服器只會發一條訊息給消費者(能者多勞模式)
channel.basicQos(1);
//上面的部分,與Test01是一樣的
//宣告一個消費者,配置好獲取訊息的方式
// Consumer consumer= new DefaultConsumer(channel){
// @Override
// public void handleDelivery(String consumerTag,Envelope envelope,AMQP.BasicProperties properties,byte[] body) throws IOException{
// String message = new String(body, "UTF-8");
// System.out.println("Customer Received '" + message + "'");
// }
// };
boolean autoAck = false;//手動確認ACk
//channel.basicConsume(queueName, autoAck, consumer) ;
channel.basicConsume(queueName, autoAck, "myConsumerTag",
new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException
{
String message = new String(body, "UTF-8");
System.out.println("Customer Received '" + message + "'");
long deliveryTag = envelope.getDeliveryTag();
// (process the message components here ...)
channel.basicAck(deliveryTag, false);
}
});
}
}
程式碼親測可以執行。並且持久化成功到本地,需要多個消費者可以用多執行緒實現。本文只作為互相學習使用,有問題歡迎大家指點!
相關文章
- Java的Socket通訊簡單例項Java單例
- Java知識整理Java
- JAVA呼叫C語言下的DLL檔案JavaC語言
- IIC知識整理以及ADS1115
- 脫殼基礎知識以及簡單應用
- 多執行緒併發鎖分類以及簡單例項執行緒單例
- C語言位運算子知識總結和例項分析C語言
- Python爬蟲筆記(一)——基礎知識簡單整理Python爬蟲筆記
- Java註解知識梳理與簡單使用Java
- Java 資料庫知識整理Java資料庫
- JAVA核心面試知識整理Java面試
- opengl簡單入門例項
- Spark 簡單例項(基本操作)Spark單例
- Java集合/陣列排序知識與IO流結合例項Java陣列排序
- rabbitmq 知識點MQ
- Python入門基礎知識例項,Python
- 例項總結Oracle知識點大全Oracle
- 整理下java六種單例模式Java單例模式
- Java基礎知識整理之this用法Java
- Java容器相關知識點整理Java
- 整理Java基礎知識--Calendar 類Java
- HTML簡單知識的總結HTML
- Linux簡單知識點Linux
- 唯一標識 Java 執行的例項Java
- C#out引數的簡單例項C#單例
- EventBus詳解及簡單例項單例
- C++學習隨筆——簡單的單例設計模式例項C++單例設計模式
- RabbitMQ基礎知識MQ
- Java基礎知識整理之註解Java
- java框架之Hibernate框架知識點整理。Java框架
- 一些知識點的整理以及面試題記錄面試題
- ElasticSearch客戶端簡單操作例項Elasticsearch客戶端
- 數學知識-核函式的通俗解釋例項函式
- 簡單介紹Go 語言單例模式Go單例模式
- go語言下快速使用GRPCGoRPC
- Java基礎知識整理之程式碼塊Java
- 整理Java基礎知識--Number&Math類Java
- JVM--Java核心面試知識整理(一)JVMJava面試