(一)發佈-訂閱模型(Publish/Subscribe)
和前兩個的模型結構不同,在發佈訂閱模型中多了一個X(exchange),exchange是一個交換機,生產者不是直接將消息發送給隊列,而是先發送給交換機。消費者可以通過隊列去訂閱這個交換機,每個消費者對應於自己的一個隊列。
這個結構就好像我們訂閱微信公衆號一樣,作者將文章發送到自己的公衆號上,只有訂閱過該公衆號的人才能收到消息。因此這個模型被稱爲發佈-訂閱模型。
(二)發佈-訂閱模型實踐
發佈訂閱模型中多了交換機的存在,而我們在rabbitmq的可視化界面中就見到過exchange
繼續通過代碼展示該模型:
2.1 工具類
public class ConnectionUtil {
public static Connection getConnection() throws IOException, TimeoutException {
//定義一個連接工廠
ConnectionFactory factory=new ConnectionFactory();
//設置服務地址
factory.setHost("127.0.0.1");
//設置AMQP端口
factory.setPort(5672);
//設置VHOSTS
factory.setVirtualHost("/vhosts_sdxb");
//設置用戶名
factory.setUsername("user_sdxb");
factory.setPassword("123456");
return factory.newConnection();
}
}
2.2 生產者
public class Sent {
private static final String EXCHANGE="ps_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//綁定交換機,設置類型爲fanout
channel.exchangeDeclare(EXCHANGE,"fanout");
String msg="hello world";
channel.basicPublish(EXCHANGE,"",null,msg.getBytes());
channel.close();
connection.close();
}
}
生產者在發佈訂閱模型中不再綁定隊列,而是綁定交換機。exchange的種類有4中,分別是Direct 、Fanout 、Topic、Headers。接下來會做詳細介紹。
2.3 消費者一
public class Receive1 {
private static final String QUEUE_NAME="ps_queue1";
private static final String EXCHANGE="ps_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//將隊列和交換機綁定
channel.queueBind(QUEUE_NAME,EXCHANGE,"");
//保證一次只分發一次
channel.basicQos(1);
Consumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg=new String(body,"utf-8");
System.out.println(msg);
//設置手動回執
channel.basicAck(envelope.getDeliveryTag(),false);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//關閉自動回覆
boolean autoAck=false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
消費者的代碼和之前一樣,唯一的區別是增加了隊列和交換機的綁定channel.queueBind();
2.4 消費者二
public class Receive2 {
private static final String QUEUE_NAME="ps_queue2";
private static final String EXCHANGE="ps_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//將隊列和交換機綁定
channel.queueBind(QUEUE_NAME,EXCHANGE,"");
//保證一次只分發一次
channel.basicQos(1);
Consumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg=new String(body,"utf-8");
System.out.println(msg);
//設置手動回執
channel.basicAck(envelope.getDeliveryTag(),false);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//關閉自動回覆
boolean autoAck=false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
2.5 運行項目
由於此時rabbitmq中不存在名稱爲ps_exchange的交換機,因此我們可以手動在rabbitmq的可視化界面中創建,也可以運行一次生產者來創建交換機。接着運行兩個消費者和生產者,可以看到生產者發送出去的消息被消費者收到。
觀察此時的可視化界面,可以看到該交換機上已經綁定了兩個隊列:
(三)Exchange類型介紹
exchange的種類有4中,分別是Direct 、Fanout 、Topic、Headers。接下來會做詳細介紹。
3.1 Fanout(不處理路由鍵)
直接將消息路由到所有綁定的隊列中,無須對消息的routingkey進行匹配操作。即交換機將消息從生產者獲取之後,直接發給訂閱的隊列。
3.2 Direct(處理路由鍵)
交換機和隊列綁定時會設置路由鍵(routingkey),當消息從生產者發送給交換機時也會發送一個路由鍵。只有當這兩個路由鍵相同時,交換機纔會把消息發送給隊列。
3.3 Topic(可以有通配符)
Topic和Direct類似,只不過Direct要求路由鍵完全相同,但是Topic可以使用通配符進行匹配,如#,*