发布——订阅(Publish/Subscribe)
工作队列背后的假设是,每个任务都恰好交付给一个工人。在这一部分中,我们将做一些完全不同的事情-我们将相同消息传达给多个消费者。这种模式称为“发布/订阅”。
通过交换机完成此模式,生产者——》交换机——》多个队列!!!——》不同的消费者。达成不同消费者接收同一台消息。
利用广播类型的交换机,交换机自动生成排他队列 ,消费者与交换机绑定,交换机与生成的队列绑定,消费者监听绑定交换机生成的一个队列!达到将一条消息发给多个消费者的目的。
生产者
/*
* 发布/订阅队列-广播模式-生产者
* */
public class Send {
//交换机名称
private final static String EXCHANGE_NAME = "ps_fanout";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("");
factory.setUsername("");
factory.setPassword("");
factory.setVirtualHost("");
//try cache新写法
try (
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel()) {
//绑定交换机 交换机名字 交换机类型:广播
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
//message内容
String message = "Hello World!ps_fanout";
//发送消息 交换机
channel.basicPublish(EXCHANGE_NAME,"", null, message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + message + "'");
}
}
}
消费者
/*
* 布/订阅队列-广播模式-消费者1
* */
public class Recv {
private final static String EXCHANGE_NAME = "ps_fanout";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("");
factory.setUsername("");
factory.setPassword("");
factory.setVirtualHost("");
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//绑定交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
//获取队列名
String queueName = channel.queueDeclare().getQueue();
//将交换机和队列绑定
channel.queueBind(queueName, EXCHANGE_NAME, "");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
//拿到消息
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
//监听队列 消费消息
//队列名称 自动回执
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
}
}
优缺点
实时上从工作队列的公平模式开始就不存在缺点了,只是有的需求无法实现,对于发布订阅模式无法实现将消息发给特定消费者的需求,因为是群发!所以需要路由模式。
路由模式(Routing)
根据routing-key绑定交换机和队列,发送消息时携带routing-key,交换机根据routing-key发送到指定队列。
生产者
/*
* 路由队列-生产者
* */
public class Send {
//交换机名称
private final static String EXCHANGE_NAME = "routing_direct";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("");
factory.setUsername("");
factory.setPassword("");
factory.setVirtualHost("");
//try cache新写法
try (
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel()) {
//绑定交换机 交换机名字 交换机类型:广播
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//message内容
String info_message = "Hello World!info";
String waring_message = "Hello World!waring";
String error_message = "Hello World!error";
String infoRoutingKey="info";
String waringRoutingKey="waring";
String errorRoutingKey="error";
//发送消息 交换机
channel.basicPublish(EXCHANGE_NAME,infoRoutingKey, null, info_message.getBytes(StandardCharsets.UTF_8));
channel.basicPublish(EXCHANGE_NAME,waringRoutingKey, null, waring_message.getBytes(StandardCharsets.UTF_8));
channel.basicPublish(EXCHANGE_NAME,errorRoutingKey, null, error_message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + info_message + "'");
System.out.println(" [x] Sent '" + waring_message + "'");
System.out.println(" [x] Sent '" + error_message + "'");
}
}
}
消费者
/*
* 路由队列-消费者1
* */
public class Recv {
private final static String EXCHANGE_NAME = "routing_direct";
public static void main(String[] argv) throws Exception {
//创建连接工厂 port默认不用配置
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("");
factory.setUsername("");
factory.setPassword("");
factory.setVirtualHost("");
//用连接工厂 创建连接 Connection继承了Closeable接口可以自动关闭
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//绑定交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//获取队列名
String queueName = channel.queueDeclare().getQueue();
//将交换机和队列绑定
String errorRoutingkey="error";
channel.queueBind(queueName, EXCHANGE_NAME, errorRoutingkey);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
//拿到消息
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
//监听队列 消费消息
//队列名称 自动回执
channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
}
}
在不指定交换机时使用默认交换机,类型direct!routingkey是队列名称。