RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

在前面的两篇博客中RabbitMQ入门:HelloRabbitMQ代码实例RabbitMQ入门:工作队列(WorkQueue)遇到的实例都是一个消息只发送给一个消费者(工作者),他们的消息

大家好,又见面了,我是全栈君。

在前面的两篇博客中

遇到的实例都是一个消息只发送给一个消费者(工作者),他们的消息模型分别为(P代表生产者,C代表消费者,红色代表队列):

RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

这次我们来看下将一个消息发送给多个消费者(工作者),这种模式一般被称为“发布/订阅”模式。其工作模型为(P代表生产者,X代表Exchange(路由器/交换机),C代表消费者,红色代表队列):

RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

我们发现,工作模型中首次出现路由器,并且每个消费者有单独的队列。生产者生成消息后将其发送给路由器,然后路由器转送到队列,消费者各自到自己的队列里面获取消息进行消费。在实际的应用场景中,生产者一般不会直接将消息发送给队列,而是发送给路由器进行中转,Exchange必须清楚的知道怎么处理收到的消息:是将消息发送到一个特定队列还是多有队列,或者直接废弃消息。这种才符合RabbitMQ消息模型的核心思想。

接下来我们详细展开今天的话题:

一、Exchange

Exchange在我们的工作模型中首次出现,因此需要详细介绍下。

Exchange分为4种类型:

Direct:完全根据key进行投递的,例如,绑定时设置了routing key为”abc”,那么客户端提交的消息,只有设置了key为”abc”的才会投递到队列。
Topic:对key进行模式匹配后进行投递,符号”#”匹配一个或多个词,符号”*”匹配正好一个词。例如”abc.#”匹配”abc.def.ghi”,”abc.*”只匹配”abc.def”。
Fanout:不需要key,它采取广播模式,一个消息进来时,投递到与该交换机绑定的所有队列。
Headers:我们可以不考虑它。

今天我们的实例采用fanout类型的exchange。

尽管首次出现,但是其实我们前面的案例中也有用到exchange,只是我们没有给他名字,用的是RabbitMQ默认的,比如下面这段代码,我们将路由器名这个参数传入了“”,如果我们需要自己声明exchange的话,这个就不能传入“”了,而是传入自己定义好的值。

RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

二、临时队列

前面两篇博客中,我们都在使用队列的时候给出了定义好的名字,这在生产者和消费者共用相同队列的时候很有必要,但是我们有了exchange,生产者不需要知道有哪些队列,因此队列名字可以不用指定了,而是通过RabbitMQ 接口自己去生成临时队列,队列名字也由RabbitMQ自动生成。通过

RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

可以声明一个非持久的、通道独占的、自动删除的队列,getQueue()方法可以获取随机队列名字。这个名字用来在队列和exchange之间建立binding关系的时候使用:

RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

 

三、代码实现

基于上面exchange和临时队列的知识铺垫,可以展开今天的代码实现了。

  1.  生产者
    public class Product {
        //exchange名字
        public static String EXCHANGE_NAME = "exchange";
    
        public static void main(String[] args) {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("localhost");
            Connection connection = null;
            Channel channel = null;
            try {
                // 1.创建连接和通道
                connection = factory.newConnection();
                channel = connection.createChannel();
    
                // 2.为通道声明exchange和exchange的类型
                channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
                
                String msg = " hello rabbitmq, this is publish/subscribe mode";
                // 3.发送消息到指定的exchange,队列指定为空,由exchange根据情况判断需要发送到哪些队列
                channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
                System.out.println("product send a msg: " + msg);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            } finally {
                // 4.关闭连接
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
    
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

     

  2. 消费者1
    public class Consumer1 {
    
        public static void main(String[] args) {
            ConnectionFactory factory = new ConnectionFactory();
            factory.setHost("localhost");
            Connection connection = null;
            Channel channel = null;
            try {
                // 1.创建连接和通道
                connection = factory.newConnection();
                channel = connection.createChannel();
    
                // 2.为通道声明exchange以及exchange类型
                channel.exchangeDeclare(Product.EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
    
                // 3.创建随机名字的队列
                String queueName = channel.queueDeclare().getQueue();
    
                // 4.建立exchange和队列的绑定关系
                channel.queueBind(queueName, Product.EXCHANGE_NAME, "");
                System.out.println(" **** Consumer1 keep alive ,waiting for messages, and then deal them");
                // 5.通过回调生成消费者并进行监听
                Consumer consumer = new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope,
                            com.rabbitmq.client.AMQP.BasicProperties properties, byte[] body) throws IOException {
    
                        // 获取消息内容然后处理
                        String msg = new String(body, "UTF-8");
                        System.out.println("*********** Consumer1" + " get message :[" + msg + "]");
                    }
                };
                // 6.消费消息
                channel.basicConsume(queueName, true, consumer);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
    }

     

  3. 消费者2,核心代码同消费者1一样,只是在日志打印上将”Consumer1″改为”Consumer2″而已。这里不再列出具体代码。
  4. 先运行消费者1和2,然后运行生产者,观察控制台log打印情况:
    生产者:
    product send a msg:  hello rabbitmq, this is publish/subscribe mode
    
    消费者1:
     **** Consumer1 keep alive ,waiting for messages, and then deal them
    *********** Consumer1 get message :[ hello rabbitmq, this is publish/subscribe mode]
    
    消费者2: **** Consumer2 keep alive ,waiting for messages, and then deal them
    *********** Consumer2 get message :[ hello rabbitmq, this is publish/subscribe mode]

    可以看到,当生产者发出消息后,两个消费者最终都收到了消息。

  5. 我们去查看RabbitMQ管理页面:RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

    在Exchanges 标签页里面多了一个名为“exchange”的路由器,他的类型是fanout。点exchange 的link进入详细页面:RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

    发现在binding项目中有了两条绑定关系,队列的名字也可以看到。将页面切换到Queues标签页:RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

    出现了两个新的队列,队列名字和绑定关系中的一样,并且队列都是自动删除的、通道独占的。

  6. 然后将消费者1和消费者2都停掉,重新查看管理页面,我们发现exchange还在,binding关系不存在了,临时队列也自动删除了RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

    RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

    RabbitMQ入门:发布/订阅(Publish/Subscribe)[通俗易懂]

     

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/120897.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • python decode函数的用法_decode函数的用法

    python decode函数的用法_decode函数的用法DECODE函数是ORACLEPL/SQL是功能强大的函数之一,目前还只有ORACLE公司的SQL提供了此函数,其他数据库厂商的SQL实现还没有此功能。DECODE有什么用途呢?先构造一个例子,假设我们想给智星职员加工资,其标准是:工资在8000元以下的将加20%;工资在8000元以上的加15%,通常的做法是,先选出记录中的工资字段值?selectsalaryintovar-salar…

    2022年7月25日
  • SSM框架下一个简单的模糊查询(超级详细)

    SSM框架下一个简单的模糊查询(超级详细)引言:模糊查询作为后台常用的一种查询方式,我们可以根据相应的关键字对其检索,从而获得所需要的记录,本次模糊查询我们通过名字的任何一个字段进行匹配查询。另外声明,源码就是以下的部分,直接复制就可以使用了。此外,想要模糊查询,最好学会分页查询,分页查询我用了两种方法,一种是利用的pageHelper,另一种没用到插件.需要源码的,或者demo,在我的资源下载,需要远程帮忙的可以加我QQ…

    2022年5月30日
  • python+selenium UI自动化不同浏览器之间的切换「建议收藏」

    python+selenium UI自动化不同浏览器之间的切换「建议收藏」python+selenium UI自动化不同浏览器之间的切换

    2022年4月22日
  • SpEL 表达式_strpbrk函数

    SpEL 表达式_strpbrk函数前言:Spring表达式语言(简称SpEL)是一种与JSP2的EL功能类似的表达式语言,它可以在运行时查询和操作对象图。与JSP2的EL相比,SpEL功能更加强大,它甚至支持方法调用和基本字符串模板函数。SpEL可以独立于Spring容器使用——只是当成简单的表达式语言来使用;也可以在Annotation或XML配置中使用SpEL,这样可以充分利用SpEL简化Spring的Bean配置。注:在…

    2022年9月12日
  • C语言结构体指针_C语言函数返回结构体指针

    C语言结构体指针_C语言函数返回结构体指针文章目录结构体概述结构体指针结构体概述问题定义:有时需要将不同类型的数据组合成一个有机的整体,以便于使用,就类似于sql中的存储一样,随着语言层次的增高封装性是越来越大的。如:intnum;charname[20];charsex;intage;charaddr[30];定义结构的一般形式为:struct结构名{成员列表};成员列表由若干个成员构成,每个成员…

    2022年10月23日
  • linux查看定时任务的命令_linux添加定时任务执行脚本

    linux查看定时任务的命令_linux添加定时任务执行脚本linux查看定时任务

    2022年10月23日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号