在上篇文章中我們提到了RabbitMQ的幾種交換器模式,我們之前的HelloWord和Work模式都是採用的默認的Exchange即Directexchange
,接下來我們這章說一下剩下的幾種模式源碼
發佈/訂閱(fanout)
發佈訂閱模式就像消費者訂閱(監聽)生產者一樣,只要生產者產生消息,消費者都可以消費
/**
* 訂閱模式
*/
public class EmitLog {
private static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
/*
* 聲明exchange(交換機)
* 參數1:交換機名稱
* 參數2:交換機類型
* 參數3:交換機持久性,如果爲true則服務器重啓時不會丟失
* 參數4:交換機在不被使用時是否刪除
* 參數5:交換機的其他屬性
*/
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
// 與前面不同, 生產者將消息發送給exchange, 而非隊列. 若發消息時還沒消費者綁定queue與該exchange, 消息將丟失
for(int i=0;i<5;i++){
String message = "Hello"+i;
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
Thread.sleep(1000);
}
channel.close();
connection.close();
}
}
public class ReceiveLogs1 {
private static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
final Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
String queueName = channel.queueDeclare().getQueue();
/*
* 綁定隊列到交換機(這個交換機的名稱一定要和上面的生產者交換機名稱相同)
* 參數1:隊列的名稱
* 參數2:交換機的名稱
* 參數3:Routing Key
*/
channel.queueBind(queueName, EXCHANGE_NAME, "");
//注意:消息發送到沒有隊列綁定的交換機時,消息將丟失,因爲,交換機沒有存儲消息的能力,消息只能存在在隊列中。
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
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(" [x] Received '" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
另一個消費者代碼一樣 我就不貼了,接下來我們運行消費者程序
消費者處在監聽狀態,接下里運行生產者生產消息
現在可以看到兩個消費者都收到了一樣的消息
Routing(direct)
我們採用路由的方式對不同的消息進行過濾,可以通過此種方式指定消息被哪些消費者消費
在生產者把消息傳遞給exchange時,需要說明消息的routing key,然後exchange根據消息的routing key匹配隊列。在說明隊列時也要說明隊列的routing key
/**
* 路由模式
*/
public class RoutingSendDirect {
private static final String EXCHANGE_NAME = "direct_logs";
// 路由關鍵字
private static final String[] routingKeys = new String[]{"info" ,"warning", "error"};
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//聲明交換機
channel.exchangeDeclare(EXCHANGE_NAME,"direct");//注意是direct
//發送信息
for (String routingKey:routingKeys){
String message = "RoutingSendDirect Send the message level:" + routingKey;
channel.basicPublish(EXCHANGE_NAME,routingKey,null,message.getBytes());
System.out.println("RoutingSendDirect Send"+routingKey +"':'" + message);
}
channel.close();
connection.close();
}
}
public class ReceiveLogsDirect1 {
// 交換器名稱
private static final String EXCHANGE_NAME = "direct_logs";
// 路由關鍵字
private static final String[] routingKeys = new String[]{"info" ,"warning"};
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//聲明交換器
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//獲取匿名隊列名稱
String queueName=channel.queueDeclare().getQueue();
//根據路由關鍵字進行綁定
for (String routingKey:routingKeys){
channel.queueBind(queueName,EXCHANGE_NAME,routingKey);
//如果想讓消費者2同時接受routingKey爲A 和爲B的消息,只要在下面在此添加一個Bing就可以了
System.out.println("ReceiveLogsDirect1 exchange:"+EXCHANGE_NAME+"," +
" queue:"+queueName+", BindRoutingKey:" + routingKey);
}
System.out.println("ReceiveLogsDirect1 Waiting for messages");
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("ReceiveLogsDirect1 Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
public class ReceiveLogsDirect2 {
// 交換器名稱
private static final String EXCHANGE_NAME = "direct_logs";
// 路由關鍵字
private static final String[] routingKeys = new String[]{"error"};
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//聲明交換器
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//獲取匿名隊列名稱
String queueName = channel.queueDeclare().getQueue();
//根據路由關鍵字進行多重綁定
for (String severity : routingKeys) {
channel.queueBind(queueName, EXCHANGE_NAME, severity);
System.out.println("ReceiveLogsDirect2 exchange:"+EXCHANGE_NAME+", queue:"+queueName+", BindRoutingKey:" + severity);
}
System.out.println("ReceiveLogsDirect2 Waiting for messages");
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("ReceiveLogsDirect2 Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
運行消費者程序
消費者分別監聽了routingKey
爲info,warning和error的消息
啓動生產者生產消息
可以看到三種routingKey
消息分別被路由到了不同消費者
Topics(topic)
Topics模式和Routing相類似,都是通過Routing Key來做消息如隊列的匹配,RabbitMQ提供兩種匹配符:
- *(星號)匹配一個
- #(井號)匹配0個或多個
/**
* 通配模式
*/
public class TopicSend {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception {
Connection connection = null;
Channel channel = null;
try{
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("localhost");
connection=factory.newConnection();
channel=connection.createChannel();
//聲明一個匹配模式的交換機
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//待發送的消息
String[] routingKeys=new String[]{
"quick.orange.rabbit",
"lazy.orange.elephant",
"quick.orange.fox",
"lazy.brown.fox",
"quick.brown.fox",
"quick.orange.male.rabbit",
"lazy.orange.male.rabbit"
};
//發送消息
for(String severity :routingKeys){
String message = "From "+severity+" routingKey' s message!";
channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes());
System.out.println("TopicSend Sent '" + severity + "':'" + message + "'");
}
}catch (Exception e){
e.printStackTrace();
if (connection!=null){
channel.close();
connection.close();
}
}finally {
if (connection!=null){
channel.close();
connection.close();
}
}
}
}
public class ReceiveLogsTopic1 {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//聲明一個匹配模式的交換機
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
//路由關鍵字
String[] routingKeys = new String[]{"*.orange.*"};
//綁定路由
for (String routingKey : routingKeys) {
channel.queueBind(queueName, EXCHANGE_NAME, routingKey);
System.out.println("ReceiveLogsTopic1 exchange:" + EXCHANGE_NAME + ", queue:" + queueName + ", BindRoutingKey:" + routingKey);
}
System.out.println("ReceiveLogsTopic1 Waiting for messages");
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("ReceiveLogsTopic1 Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
public class ReceiveLogsTopic2 {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 聲明一個匹配模式的交換器
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String queueName = channel.queueDeclare().getQueue();
// 路由關鍵字
String[] routingKeys = new String[]{"*.*.rabbit", "lazy.#"};
// 綁定路由關鍵字
for (String bindingKey : routingKeys) {
channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
System.out.println("ReceiveLogsTopic2 exchange:"+EXCHANGE_NAME+", queue:"+queueName+", BindRoutingKey:" + bindingKey);
}
System.out.println("ReceiveLogsTopic2 Waiting for messages");
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("ReceiveLogsTopic2 Received '" + envelope.getRoutingKey() + "':'" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
運行我們的消費者程序
消費者已經通過匹配的routingKey
監聽,運行生產着生產消息
可以看到消費者已經根據routingKey
消費到對應通配的消息
小結
我們上面的三種模式具有以下特點
- 生產者聲明瞭
exchange
的類型,生產者種並沒有生命隊列,而是在消費者中由服務器隨機生成的 - 我們的隊列都是在消費者中聲明的,所以要先啓動消費者進行監聽
文章參考
https://javaduqing.github.io
http://www.cnblogs.com/LipeiNet/p/5978276.html