RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

在之前的一篇博客RabbitMQ入门:认识并安装RabbitMQ(以Windows系统为例)中,我们安装了RabbitMQ并且对其也有的初步的认识,今天就来写个入门小例子来加深概念理解并了解代码怎么实

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

在之前的一篇博客RabbitMQ入门:认识并安装RabbitMQ(以Windows系统为例)中,我们安装了RabbitMQ并且对其也有的初步的认识,今天就来写个入门小例子来加深概念理解并了解代码怎么实现。

本篇博客围绕下面几个方面展开:

  1. 代码前的理论热身
  2. 代码实例:Hello RabbitMQ
  3. 运行代码并调试问题

Now, Let’s begin !

一、代码前的理论热身

我们来看张图:

RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

Publisher(生产者)生成消息,然后publish(发布)消息到exchange(路由器,也有资料翻译成交换机),然后根据路由规则将消息传递到Queue(队列),最终交由Consumer(消费者)进行消费处理。

这里的生产者和消费者都是我们的应用,因此我们的代码中要实现这两个部分。

中间的节点就是RabbitMQ 提供的内容,需要再生产者和消费者里面调用其接口来定义和使用这些节点。

 

二、代码实例:Hello RabbitMQ

  1. 首先来实现生产者,这里我没有用Publisher做类名,而是用的Provider,没有特别的用意,就是在起名字的时候不小心写成了这样,不需要在意这个细节,O(∩_∩)O。
    package com.sam.hello_rabbitmq;
    
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    public class Provider {
    
        //定义队列名
        static String QUEUE_NAME = "helloRabbit";
    
        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.为通道声明队列
                channel.queueDeclare(QUEUE_NAME, false, false, false, null);
                
                //3.发布消息
                String msg = " hello rabbitmq, welcome to sam's blog.";
                channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
                System.out.println("provider 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步中,channel.queueDeclare 用来创建队列,有5个参数:String queue, 队列名; boolean durable, 该队列是否需要持久化; boolean exclusive,该队列是否为该通道独占的(其他通道是否可以消费该队列); boolean autoDelete,该队列不再使用的时候,是否让RabbitMQ服务器自动删除掉; Map<String, Object> arguments 其他参数。第3步中,channel.basicPublish 发布消息(用在生产者),有4个参数:String exchange, 路由器(有的资料翻译成交换机)的名字,即将消息发到哪个路由器; String routingKey, 路由键,即发布消息时,该消息的路由键是什么; BasicProperties props, 指定消息的基本属性; byte[] body 消息体,也就是消息的内容,是字节数组。 可能你会疑惑,为什么没有exchange呢?因为如果声明了队列,可以不声明路由器。

  2. 接着来实现消费者,消费者实现和生产者过程差不多,但是在这里并没有关闭连接和通道,是因为要消费者一直等待随时可能发来的消息。代码如下:
    package com.sam.hello_rabbitmq;
    
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    import com.rabbitmq.client.Consumer;
    import com.rabbitmq.client.DefaultConsumer;
    import com.rabbitmq.client.Envelope;
    
    public class HelloConsumer {
    
        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.为通道声明队列
                channel.queueDeclare(Provider.QUEUE_NAME, false, false, false, null);
                System.out.println(" **** keep alive ,waiting for messages, and then deal them");
                // 3.通过回调生成消费者
                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("*********** HelloConsumer" + " get message :[" + msg +"]");
                    }
                };
                
                //4.消费消息
                channel.basicConsume(Provider.QUEUE_NAME, true, consumer);
    
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
    }

    在第4步中,channel.basicConsume 用来接收消息,用在消费者,有3个参数:String queue, 队列名字,即要从哪个队列中接收消息; boolean autoAck, 是否自动确认,默认true; Consumer callback 消费者,即谁接收消息。

 

三、运行代码并调试问题

代码写好了,接下来进行测试,

  1. 先来执行下Provider.java,发现报错了:
    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    SLF4J: Defaulting to no-operation (NOP) logger implementation
    SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
    java.io.IOException
        at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:124)
        at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:120)
        at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:142)
        at com.rabbitmq.client.impl.ChannelN.queueDeclare(ChannelN.java:952)
        at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.queueDeclare(AutorecoveringChannel.java:333)
        at com.sam.hello_rabbitmq.Provider.main(Provider.java:36)
    Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'durable' for queue 'helloRabbit' in vhost '/': received 'false' but current is 'true', class-id=50, method-id=10)
        at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:66)
        at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:36)
        at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:443)
        at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:263)
        at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:136)
        ... 3 more
    Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'durable' for queue 'helloRabbit' in vhost '/': received 'false' but current is 'true', class-id=50, method-id=10)
        at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:509)
        at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:340)
        at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:162)
        at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:109)
        at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:643)
        at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47)
        at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:581)
        at java.lang.Thread.run(Thread.java:745)
    Exception in thread "main" com.rabbitmq.client.AlreadyClosedException: channel is already closed due to channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'durable' for queue 'helloRabbit' in vhost '/': received 'false' but current is 'true', class-id=50, method-id=10)
        at com.rabbitmq.client.impl.AMQChannel.processShutdownSignal(AMQChannel.java:345)
        at com.rabbitmq.client.impl.ChannelN.startProcessShutdownSignal(ChannelN.java:286)
        at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:600)
        at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:534)
        at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:527)
        at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.close(AutorecoveringChannel.java:68)
        at com.sam.hello_rabbitmq.Provider.main(Provider.java:60)

    关键堆栈信息是:inequivalent arg 'durable' for queue 'helloRabbit' in vhost '/': received 'false' but current is 'true',说是helloRabbit这个队列durable(是否需要持久化)
    参数已经设定成了true 但是代码中指定的是false,冲突了,纳尼?访问RabbitMQ管理页面:http://localhost:15672/#/queues 发现已经存在一个队列helloRabbit,

    RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

    点helloRabbit的链接,发现队列的durable属性确实是true。哦,原来我之前在做别的练习的时候,创建过一个叫这个名字的队列了,而且属性值刚好为true.

    RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

    那么接下来删掉这个既存的队列

    RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

    再去执行Provider.java,后台打印了内容,并且队列中有了一条ready的消息。RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

    问题解决!

  2. 执行HelloConsumer.java,预想的结果是在启动后,控制台直接打印出log并且RabbitMQ管理页面没有ready的消息:RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

    RabbitMQ入门:Hello RabbitMQ 代码实例[通俗易懂]

    结果符合预期。

到此,全部工作完美结束。

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

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

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

相关推荐

  • 常用的前端JavaScript方法封装

    常用的前端JavaScript方法封装1、输入一个值,返回其数据类型functiontype(para){returnObject.prototype.toString.call(para)}2、数组去重functionunique1(arr){return[…newSet(arr)]}functionunique2(arr){varobj={};returnarr.filter(ele=>{if(!obj[ele])..

    2022年7月11日
  • 剑指offer Java_工程图学基础知识点总结

    剑指offer Java_工程图学基础知识点总结线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建(类似于工厂设计模式),使用完毕不需要销毁线程而是返回池中,从而减少创建和销毁线程对象的开销。设计一个动态大小的线程池,如何设计,应该有哪些方法?线程管理器(ThreadPool)用于创建并管理线程池,包括创建线程,销毁线程池,添加新任务;工作线程(PoolWorker)线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;任务接口(Task)任务队列(TaskQueue)创建线程池。…

    2022年8月10日
  • C语言学习——结构体_联合体_枚举用法直通[通俗易懂]

    C语言学习——结构体_联合体_枚举用法直通[通俗易懂]一、结构体嵌套#include <stdio.h>// 出生日期:年月日struct sBirthday{ int iYear; int iMonth; int iDay;};// 公民信息struct sSCitizenIDInfo{ char cName[10]; char cSex[4]; sBirthday BirthDate; // 结构体变量…

    2022年8月18日
  • 全网最详细完备的class类文件结构解析

    全网最详细完备的class类文件结构解析写在前面本文隶属于专栏《100个问题搞定Java虚拟机》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!本专栏目录结构和文献引用请见100个问题搞定Java虚拟机解答Class文件是一组以8位字节为基础单位的二进制流,不同的数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有任何空隙存在。这些数据项目由无符号数和表来存储数据,按照顺序依次是:1.魔数和Class文件的版本2.常量池3.访问标志4.类索引、父类索引与接口索引集合5.字段表

    2022年5月6日
  • redis 密码_商用密码知识

    redis 密码_商用密码知识Redis可以设置密码,设置密码后如果不用密码登录,执行命令时会提示错误:下面列举一下Redis密码相关知识。1,在配置文件中设置密码在Redis的配置文件中,可以用requirepass参数配置密码,比如这样:requirepassthisispassword那么密码就是thisispassword。配置文件中设置的密码在启动Redis时就会生效。…

    2022年9月5日
  • 正态qq图怎么判断分布_怎么判断是不是QQ小号

    正态qq图怎么判断分布_怎么判断是不是QQ小号一、正态QQ图的原理QQ图通过把测试样本数据的分位数与已知分布相比较,从而来检验数据的分布情况。[1]分位数:亦称分位点,是指将一个随机变量的概率分布范围分为几个等份的数值点。常用的有一个分位数叫,百分位数,它是指如果将一组数据从小到大排序,并计算相应的累计百分位,则某一百分位所对应数据的值就称为这一百分位的百分位数。QQ图是一种散点图,对应于正态分布的QQ图,就是由标准正态分布的分位

    2022年8月10日

发表回复

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

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