rabbitMQ学习笔记(三)——发布订阅模式

介绍

首先来段官方文档

In the previous tutorial we created a work queue. The assumption behind a work queue is that each task is delivered to exactly one worker. In this part we’ll do something completely different – we’ll deliver a message to multiple consumers. This pattern is known as “publish/subscribe”.

The core idea in the messaging model in RabbitMQ is that the producer never sends any messages directly to a queue. Actually, quite often the producer doesn’t even know if a message will be delivered to any queue at all.

Instead, the producer can only send messages to an exchange. An exchange is a very simple thing. On one side it receives messages from producers and the other side it pushes them to queues. The exchange must know exactly what to do with a message it receives. Should it be appended to a particular queue? Should it be appended to many queues? Or should it get discarded. The rules for that are defined by the exchange type.

简言之,之前的模式都是一个生产者,一个队列,一个或者多个消费者。然而发布订阅模式引入了交换机的概念。有了交换机,生产者不会将消息直接发到队列中,而是发送到交换机,由交换机来决定将消息传送到具体哪一个或者多个队列中。其模型如下:
在这里插入图片描述

生产者实现

其基本实现逻辑和第一节讲的无异,关键是多了交换机和交换机与队列之间的联系。因此在代码上,仅仅添加了声明交换机,和绑定队列。注意,这里声明了两个队列,绑定时也是绑定了两个队列。
声明交换机代码如下:

//声明一个交换机
//参数:String exchange, String type
/**
* 参数明细:
* exchange:交换机名称
* type:交换机类型
*       fanout:对应额rabbitmq工作模式是publish/subscribe
*       direct:对应额rabbitmq工作模式是routing路由模式
*       topic:对应额rabbitmq工作模式是topics通配符
*       headers:对应额rabbitmq工作模式是headers转发器
*/
channel.exchangeDeclare(EXCHANGE_FANOUT_INFO, BuiltinExchangeType.FANOUT);

绑定队列代码如下:

//进行交换机和队列绑定
//参数:String queue, String exchange, String routingKey
/**
* 参数明细:
* queue:队列名称
* exchange:交换机名称
* routingKey:路由key,作用是交换机根据路由key将消息发布到指定队列中。在发布订阅模式设为空串
*/
channel.queueBind(QUEUENAME01,EXCHANGE_FANOUT_INFO,"");

完整代码如下:

public class Producer01 {
    private static final String QUEUENAME01 = "queue01";
    private static final String QUEUENAME02 = "queue02";
    private static final String EXCHANGE_FANOUT_INFO = "exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        //通过连接工厂创建新的连接和mq建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //设置虚拟机,一个mq的服务可以设置多个虚拟机,每个虚拟机相当于一个独立的mq。
        connectionFactory.setVirtualHost("/");
        //建立新连接
        Connection connection = null;
        connection = connectionFactory.newConnection();
        //创建会话通道,生产者和mq服务所有的通信都在channel里完成。
        Channel channel = connection.createChannel();
        //声明队列:如果队列在mq中没有,则创建
        //参数String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
        /**
         * 参数明细:
         * queue:队列名称
         * durable:持久化,如果持久化,重启mq服务队列还在
         * exclusive:是否独占连接,队列只允许在该连接中访问,如果连接关闭则队列删除,设置为true,可用于临时队列。
         * autoDelete:自动删除,设置为true,连接不使用则删除队列
         * arguments:指定一些扩展参数。例如存活时间等等
         */
        channel.queueDeclare(QUEUENAME01,true,false,false,null);
        channel.queueDeclare(QUEUENAME02,true,false,false,null);
        //声明一个交换机
        //参数:String exchange, String type
        /**
         * 参数明细:
         * exchange:交换机名称
         * type:交换机类型
         *       fanout:对应额rabbitmq工作模式是publish/subscribe
         *       direct:对应额rabbitmq工作模式是routing路由模式
         *       topic:对应额rabbitmq工作模式是topics通配符
         *       headers:对应额rabbitmq工作模式是headers转发器
         */
        channel.exchangeDeclare(EXCHANGE_FANOUT_INFO, BuiltinExchangeType.FANOUT);
        //进行交换机和队列绑定
        //参数:String queue, String exchange, String routingKey
        /**
         * 参数明细:
         * queue:队列名称
         * exchange:交换机名称
         * routingKey:路由key,作用是交换机根据路由key将消息发布到指定队列中。在发布订阅模式设为空串
         */
        channel.queueBind(QUEUENAME01,EXCHANGE_FANOUT_INFO,"");
        channel.queueBind(QUEUENAME02,EXCHANGE_FANOUT_INFO,"");
        //发送消息
        //参数:String exchange, String routingKey, BasicProperties props, byte[] body
        /**
         * 参数明细:
         * exchange:交换机,不指定的话为默认交换机
         * routingKey:交换机根据路由key,将消息转发到指定队列,如果使用默认交换机,routingKey设置为队列的名称
         * props:消息的属性
         * body:消息内容,字节数组形式
         */
        channel.basicPublish(EXCHANGE_FANOUT_INFO,"",null,"现在是发布订阅模式".getBytes());
        System.out.println("send to mq");

        //关闭通道
        channel.close();
        //关闭连接
        connection.close();
    }

}

消费者实现

消费者实现同样也要声明交换机和绑定队列,这次只需要绑定一次(对应的)队列即可。

public class Consumer01 {

    private static final String QUEUENAME01 = "queue01";
    private static final String EXCHANGE_FANOUT_INFO = "exchange";

    public static void main(String[] args) throws IOException, TimeoutException {
        //通过连接工厂创建新的连接和mq建立连接
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        //设置虚拟机,一个mq的服务可以设置多个虚拟机,每个mq相当于一个独立的mq。
        connectionFactory.setVirtualHost("/");
        //建立新连接
        Connection connection = null;
        connection = connectionFactory.newConnection();
        //创建会话通道,生产者和mq服务所有的通信都在channel里完成。
        Channel channel = connection.createChannel();

        //声明队列:如果队列在mq中没有,则创建
        channel.queueDeclare(QUEUENAME01,true,false,false,null);

        channel.exchangeDeclare(EXCHANGE_FANOUT_INFO, BuiltinExchangeType.FANOUT);

        channel.queueBind(QUEUENAME01,EXCHANGE_FANOUT_INFO,"");

        //实现消费方法
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){

            /**
             * 当接收到消息后,此方法执行
             * @param consumerTag 消费者标签,用来标识消费者,在监听队列时也可设置channel.basicConsume
             * @param envelope 信封,可用信封获取一些信息,例如交换机、消息id等
             * @param properties 消息属性,发送消息时设置的消息属性可在这里获取
             * @param body 消息内容
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //super.handleDelivery(consumerTag, envelope, properties, body);
                //拿到交换机
                String exchange = envelope.getExchange();
                //消息id,mq在channel中用来表示消息的id,可用于确认消息已接收。
                long deliveryTag = envelope.getDeliveryTag();
                String message = new String(body,"UTF-8");
                System.out.println("交换机:"+exchange+"  消息id"+deliveryTag+"  消息内容"+message);
            }
        };

        //监听队列
        //参数 String queue, boolean autoAck, Consumer callback
        /**
         * 参数明细:
         * queue:队列名称
         * autoAck:自动回复,消费者接受到消息会自动告诉mq服务消息已接收此参数设置为true
         *          会自动回复mq,如果设置为false要通过编程回复,不回复的话消息一直在队列里。
         * callback:回调方法,当消费者接收到消息要执行的方法。
         */
        channel.basicConsume(QUEUENAME01,true,defaultConsumer);
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值