一、概念和模型
- 发布订阅模式,同一条信息发送给多个消费者。该模式是通过加入路由得以实现的,消息生产者将信息发送到管道,管道将消息发送给与之绑定的队列,然后消费者从相应的队列中获取消息并进行处理。场景可用于微信公众号等功能。
二、Java代码实现
- 创建MQ连接工具类
public class RabbitmqUntil {
//获取连接
public static Connection getRabbitmqConnection() throws Exception{
//定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("127.0.0.1");
//设置端口(这里的端口号指定是AMQP协议所用的端口号)
factory.setPort(5672);
//设置数据库
factory.setVirtualHost("/test");
//设置用户名
factory.setUsername("test");
//设置密码
factory.setPassword("test");
return factory.newConnection();
}
}
- 创建消息生产者
public class PublishSender {
//路由名称
public static final String ROUNTING_NAME="publish rounting";
public static void sendMessage()throws Exception{
//1、获取连接
Connection rabbitmqConnection = RabbitmqUntil.getRabbitmqConnection();
//2、创建通道
Channel channel = rabbitmqConnection.createChannel();
//3、创建路由(参数1:路由名称;参数2:模式名称,fanout表示发布订阅模式)
channel.exchangeDeclare(ROUNTING_NAME,"fanout");
//4、设置每次发送信息不超过1条
channel.basicQos(1);
String message="hello ,I am Publish/Subscribe";
//5、发布消息
channel.basicPublish(ROUNTING_NAME,"",null,message.getBytes());
}
public static void main(String[] args)throws Exception {
PublishSender.sendMessage();
}
}
注:创建路由时channel.exchangeDeclare(ROUNTING_NAME,"fanout");第二个参数必须是“fanout”,表示该模式为发布订阅模式
- 消费者一
public class publishReceiver1 {
//路由名称名称
public static final String ROUNTING_NAME="publish rounting";
//队列名称
public static final String PUBLISH_ROUNTING_QUEQU="publish rounting queue1";
public static void receiveMessage() throws Exception{
//1、创建连接
Connection rabbitmqConnection = RabbitmqUntil.getRabbitmqConnection();
//2、获取通道
final Channel channel = rabbitmqConnection.createChannel();
//3、定义队列
channel.queueDeclare(PUBLISH_ROUNTING_QUEQU,true,false,false,null);
//4、设置每次处理信息不超过1条
channel.basicQos(1);
//5、将路由和队列进行绑定
channel.queueBind(PUBLISH_ROUNTING_QUEQU,ROUNTING_NAME,"");
//6、定义消费用户
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try{
System.out.println("消费者1:"+new String(body,"utf-8"));
}catch (Exception e){
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//7、监听队列消息
channel.basicConsume(PUBLISH_ROUNTING_QUEQU,false,defaultConsumer);
}
public static void main(String[] args)throws Exception {
publishReceiver1.receiveMessage();
}
}
- 消费者二
public class publishReceiver2 {
//路由名称名称
public static final String ROUNTING_NAME="publish rounting";
//队列名称
public static final String PUBLISH_ROUNTING_QUEQU="publish rounting queue2";
public static void receiveMessage() throws Exception{
//1、创建连接
Connection rabbitmqConnection = RabbitmqUntil.getRabbitmqConnection();
//2、获取通道
final Channel channel = rabbitmqConnection.createChannel();
//3、定义队列
channel.queueDeclare(PUBLISH_ROUNTING_QUEQU,true,false,false,null);
//4、设置每次处理信息不超过1条
channel.basicQos(1);
//5、将路由和队列进行绑定
channel.queueBind(PUBLISH_ROUNTING_QUEQU,ROUNTING_NAME,"");
//6、定义消费用户
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try{
System.out.println("消费者2:"+new String(body,"utf-8"));
}catch (Exception e){
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//7、监听队列消息
channel.basicConsume(PUBLISH_ROUNTING_QUEQU,false,defaultConsumer);
}
public static void main(String[] args)throws Exception {
publishReceiver2.receiveMessage();
}
}
注:第5步将路由器与队列进行绑定,这样消费者就可以丛对应的队列中获取到信息
- 运行三个类的main方法后,可以看到消费者都可以接受到消息,并且在rabbitmq控制台可以看到路由器与队列的关系
注:路由器没有存储功能,所以在没有与之绑定的队列之前,执行发送消息,数据会丢失。