RabbitMQ(一)

TCP/IP三次握手 四次挥手
https://blog.csdn.net/weixin_42153410/article/details/80277733
6
RabbitMQ是一个开源的遵循AMQP协议的基于Erlang语言编写,支持多种客户端(语言),用于在分布式系统中存储消息,转发消息,具有高可用,高可扩性,易用性等特征

消息的分发

轮询分发(公平,无数据倾斜)

公平分发(数据倾斜,性能好的消费得多一些)

消息分发策略的机制和对比

ActiveMQ RabbitMQ Kafka RocketMQ
发布订阅 支持 支持 支持 支持
轮询分发 支持 支持 支持 -
公平分发 - 支持 支持 -
重发 支持 支持 - 支持
消息拉取 - 支持 支持 支持

消息队列的高可用和高可靠

什么是高可用机制?

高可用:指产品在规定的条件和规定的时间内处于可执行规定功能状态的能力

当业务量增大时,请求也过大,一台消息中间件服务器会触及硬件(CPU、内存、磁盘)的极限,一台消息服务器已经无法满足业务的需求,所以消息中间件必须支持集群部署,来达到高可用的目的

集群模式

1.master-slave主从共享 (单写多度)
2.master-slave主从同步(单写多度)
3.多主集群同步,多主多从(多写多度)
4.多主集群转发,
5.master-slave与Breoker-cluster的组合方案

要么消息共享,要么消息同步,要么元数据共享

什么是高可靠机制?

指系统可以无故障低持续运行,比如一个系统突然崩溃,报错,异常等等并不影响线上业务的正常运行,出错的几率极低,则称之为高可靠。
在高并发场景中,如不能保证系统的高可靠,那造成的隐患和损失非常严重。

如何保证中间件消息的可靠性?
1.消息的传输:通过协议来保证系统间数据解析的正确性
2.消息的存储可靠:通过持久化来保证消息的可靠性

##安装使用
安装rabbitmq
安装erlang

手动启动方法一:systemctl start rabbitmq-server
重启服务:systemctl restart rabbitmq-server
关闭服务:rabbitmqctl stop

查看rabbitmq

systemctl status rabbitmq-server

rabbitmq图形化界面默认为ip:15672
要设置5672和15672为安全端口否则访问不了
RabbitMQ(一)_第1张图片
RabbitMQ(一)_第2张图片

rabbitmqctl add_user user pass #新增用户
rabbitmqctl set_user_tags user 角色 #给用户赋予角色
rabbitmqctl set change_password user 新密码  #修改密码
rabbitmqctl list_users  #可查看有哪些用户

rabbitmq角色分类

  1. none
    不能访问management plugin

  2. management:查看自己相关的节点
    列出自己可以通过AMQP登入的虚拟机

    • 查看自己的虚拟机节点Virtual hosts的queues,exchanges和bingings信息
    • 查看和关闭自己的channels和connections
    • 查看有关自己的虚拟机节点virtual hosts的统计信息。包括其他用户在这个节点virtual hosts中的活动信息
  3. Policymaker

    • 包含management的所有权限
    • 查看和创建和删除自己的virtual hosts所属的policies和parameters信息
  4. Monitoring

  • 包含management的所有权限
  • 罗列出所有的virtual hosts,包括不能登录的virtual hosts
  • 查看其他用户的connections和channels信息
  • 查看节点级别的数据如clustering和memory使用情况
  • 查看所有的virtual hosts的全局统计信息
  1. Administrator
    • 最高权限
    • 可以创建和删除virtual hosts
    • 可以查看,创建和删除users
    • 查看创建permisssions

简单的生产者消费者

生产者

public class Producer {
    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("ip");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("user");
        connectionFactory.setPassword("mima");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();
            //4.通过通道创建交换机,声明队列,绑定关系,路由key,发送消息,和接收消息
            String queueName = "queue3";
            /**
             * @Param1 队列的名称
             * @Param2 是否要持久化,durable=false,所谓持久化消息是否存盘,如果false 非持久化 true 持久化 非持久化会存盘吗?会存盘,会随着服务器重启而丢失,持久化队列重启不会丢失
             *  @Param3 排他性,是否是一个独占队列
             *  @Param4 是否自动删除,随着最后一个消费者消息完毕消息以后是否把队列自动删除,false,不删除,true自动删除
             *  @Param5 携带附属参数
             */
            channel.queueDeclare(queueName,false,false,true,null);
            //channel.queueDeclare(queueName,false,false,false,null);
            //5.准备消息内容
            String message = "HElLO RABBITMQ";
            //6.发送消息给队列
            channel.basicPublish("",queueName, null,message.getBytes());
            System.out.println("消息发送成功咯");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
             //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }


    }
}

消费者

public class Consumer {
    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("ip");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("user");
        connectionFactory.setPassword("mima");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();

            /**
             * @Param1 要接收的队列名
             */
            channel.basicConsume("queue3", true, new DeliverCallback() {
                public void handle(String consumerTag, Delivery message) throws IOException {
                    System.out.println("消费者收到的信息是:" + new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                public void handle(String s) throws IOException {
                    System.out.println("消费者接收消息失败了。。。。");
                }
            });
            System.out.println("开始接收消息");
            //程序不向下执行
            System.in.read();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

RabbitMQ(一)_第3张图片
queue1没有设置自动删除,也没有持久化
queue2设置了持久化features==》 D==>durable:true 持久化队列重启后还在
queue3自动删除配置的true ,就有一个features==》 AD==>auto-delete:true

若原本消息设置的auo-delete是false直接改为true,会报错

java.io.IOException
	at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:129)
	at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:125)
	at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:147)
	at com.rabbitmq.client.impl.ChannelN.queueDeclare(ChannelN.java:968)
	at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.queueDeclare(AutorecoveringChannel.java:342)
	at com.doudou.simple.Producer.main(Producer.java:44)
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'auto_delete' for queue 'queue1' in vhost '/': received 'true' but current is 'false', 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:502)
	at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:293)
	at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:141)
	... 3 more
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'auto_delete' for queue 'queue1' in vhost '/': received 'true' but current is 'false', class-id=50, method-id=10)
	at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:517)
	at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:341)
	at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:182)
	at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:114)
	at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:739)
	at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47)
	at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:666)
	at java.lang.Thread.run(Thread.java:748)

rabbitmq为什么是基于通道去处理而不是链接呢?

大家都知道,在使用rabbitmq时不管是消费还是生产都需要创建信道(channel) 和connection(连接),如下图producer示例。我们完全可以直接使用Connection就能完成信道的工作,为什么还要引入信道呢,试想这样一个场景,一个应用有多个线程需要从rabbitmq中消费,或是生产消息,那么必然会建立很多个connection ,也就是多个tcp连接,对操作系统而言,建立和销毁tcp连接是很昂贵的开销,如果遇到使用高峰,性能瓶颈也随之显现,rabbitmq采用类似nio的做法,连接tcp连接复用,不仅可以减少性能开销,同时也便于管理。

每个线程都把持一个信道,所以信道复用了TCP连接。同时rabbitmq可以确保每个线程的私密性,就像拥有独立的连接一样。当每个信道的流量不是很大时,复用单一的connection可以再产生性能瓶颈的情况下有效地节省tcp连接资源,但是当信道本身的流量很大时,这时候多个信道复用一个connection就会产生性能瓶颈,进而是整体的流量被限制了。此时就需要开辟多个connection,将这些信道均摊到这些connection中,至于这些相关调优策略需要根据业务自身的实际情况进行调节。
RabbitMQ(一)_第4张图片

可以存在没有交换机的队列吗?

不可能,虽然没有指定交换机,但一定会存在一个默认的交换机

RabbitMQ的核心组成部分

RabbitMQ(一)_第5张图片

交换机(Exchange)一定是要有的,如果没写的话,则是会使用默认的交换机

核心概念
Server: 又被称为Broker,接受客户端的连接,实现AMQP服务。就是我们自己安装的rabbitmq-server
Connection: 连接,应用程序与Broker的网络连接(使用的是TCP/IP连接)
Channel: 网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道,每个通道Channel代表一个会话任务。
Message:消息,服务于应用程序之间传递的数据,由Properties和body组成,Properties可以对消息进行修饰,比如消息的优先级,延迟等高级特征,Body则是消息体的内容。
Virtual Host 虚拟地址,用于逻辑层隔离,最上层的消息路由,一个虚拟机理由可以有若干的Exchange和Queueu,同一个虚拟机里面不能有相同名字的Exchange。
Exchange: 交换机,接收消息,根据路由键发送消息到绑定的队列(不具备储存消息的能力)
Bindings: Exchange和Queue之间的虚拟连接,Binding中可以保护多个routing key.
Routing key: 是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息。
Queue:队列,也是MessageQueue队列,保存消息转发给消费者

RabbitMQ的整体架构

RabbitMQ(一)_第6张图片

RabbitMQ的操作流程
第一:获取Conection
第二:获取Channel
第三:定义Exchange,Queue
第四:使用一个RoutingKey将Queue Binding到一个Exchange上
第五:通过指定一个Exchange和一个RoutingKey来将消息发送到对应的Queue上,
第六:Consumer在接收时也是获取connection,接着获取channel,然后指定一个Queue,到Queue上取消息,它对Exchange,RoutingKey及如何Binding都不关心,到对应的Queue上去取消息就行了。

注意:一个PublisherClient发送消息,哪些ConsumerClient可以收到消息,在于Exchange,RoutingKey,Queue的关系上

RabbitMQ的运行流程

RabbitMQ(一)_第7张图片

交换机的四种类型
RabbitMQ(一)_第8张图片

  1. fanout
    找出绑定的队列,不再判断routing key

  2. direct
    先获取绑定的队列,然后还会再判断routing key;且routingkey需是完全匹配模式

  3. topic
    匹配bindKey和routingKey,但routing key的规则是模糊匹配模式

  4. headers
    不 匹配bindingKey和routingKey,而是在绑定队列与交换器的时候指定一个键值对;当交换器在分发消息的时候会先解开消息体里的headers数据,然后判断里面是否有所设置的键值对,如果发现匹配成功,才将消息分发到队列中;这种交换器类型在性能上相对来说较差,在实际工作中很少会用到。

从消息分发的性能上来比较:fanout > direct > topic > headers
topic的匹配规则只是用于消费者而不是生产者

通配符

路由键以.为分隔符,每一个分隔符的代表一个单词
通配符*匹配一个单词、通配符#可以匹配多个单词
*可以在routingKey和bindKey上使用,#只能用于RoutingKey中

RabbitMQ支持消息的模式

参考官网:https://www.rabbitmq.com/getstarted.html
默认交换机的模式是direct模式
在这里插入图片描述

  1. 简单模式 Simple
    .
    在这里插入图片描述

  2. 工作模式 Work
    RabbitMQ(一)_第9张图片
    当有多个消费者接入时,消息如何分配

    • 轮询模式:一个消费者一条按均分配;不会根据你的处理速度等有所倾斜
    • 公平分发:根据消费者的能力进行公平分发;处理快的处理的多;处理慢的处理得少;按劳分配;
  3. 发布订阅模式
    RabbitMQ(一)_第10张图片
    创建交换机的模式为fanout
    RabbitMQ(一)_第11张图片
    即是队列订阅到同一个交换机,生产者发送一条消息,订阅者均可收到

  4. 路由模式
    Receiving messages selectively
    RabbitMQ(一)_第12张图片
    相比于订阅模式制订了路由key,即是什么条件下发到什么队列
    创建交换机的模式为direct,如果用fanout全部都会收到
    RabbitMQ(一)_第13张图片
    RabbitMQ(一)_第14张图片
    指定发送的路由key
    RabbitMQ(一)_第15张图片

  5. 主题Topic模式
    Receiving messages based on a pattern (topics)
    RabbitMQ(一)_第16张图片
    即是路由模式的升级;支持模糊匹配的路由;
    创建交换机类型选择topic
    RabbitMQ(一)_第17张图片
    #代表0级或1级或多级
    *代表有且只有一级
    发送指定路由key为order只有queue3可以收到
    发送指定路由key为course只有queue2不能收到
    发送指定路由key为.course.queue2可以收到
    com.course.order=>queue1,queue2,queue3均可收到
    com.course.order.xxx.xx=>queue1,queue3可收到;queue2收不到

  6. 参数模式
    RabbitMQ(一)_第18张图片

根据参数为多少去匹配:
创建交换机模式选择为headers
RabbitMQ(一)_第19张图片

RabbitMQ使用场景

解耦、削峰、异步

用代码去操作绑定等

例子为direct模式
生产者

public class Producer {
    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("ip");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("user");
        connectionFactory.setPassword("mima");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();

            //5.准备消息内容
            String message = "HElLO RABBITMQ routing code direct模式";
            //准备交换机
            String exchangeName = "cdxx-direct-exchange-code";
            //路由key-模糊匹配路由
            String routeKey = "course";
            //指定交换机类型
            String type = "direct";
            //6.代码声明交换机
            /**
             * @Param1:交换机的名字
             * @Param2:交换机的类型
             * @Param3: 是否持久化,true持久化,持久化后rabbitmq重启不会消失
             */
            channel.exchangeDeclare(exchangeName,type,true);

            //7.声明队列
            /**
             * @Param1 队列的名称
             * @Param2 是否要持久化,durable=false,所谓持久化消息是否存盘,如果false 非持久化 true 持久化 非持久化会存盘吗?会存盘,会随着服务器重启而丢失,持久化队列重启不会丢失
             *  @Param3 排他性,是否是一个独占队列
             *  @Param4 是否自动删除,随着最后一个消费者消息完毕消息以后是否把队列自动删除,false,不删除,true自动删除
             *  @Param5 携带附属参数,headers模式下匹配参数
             */
            channel.queueDeclare("codequeue5",true,false,false,null);
            channel.queueDeclare("codequeue6",true,false,false,null);
            channel.queueDeclare("codequeue7",true,false,false,null);
            //8.绑定队列和交换机关系
            /**
             * @Param1 队列的名称
             * @Param2 交换机的名称
             * @Param3 routing key
             */
            channel.queueBind("codequeue5",exchangeName,"order");
            channel.queueBind("codequeue6",exchangeName,"order");
            channel.queueBind("codequeue7",exchangeName,"course");
            //6.发送消息给队列
            /**
             * @Param1:交换机 可以存在没有交换机的队列吗?不可能,虽然没有指定交换机,但一定会存在一个默认的交换机
             * @Param2:路由key
             * @Param3: 消息的状态控制
             * @Param4:消息主题
             */
            channel.basicPublish(exchangeName,routeKey, null,message.getBytes());
            System.out.println("消息发送成功咯");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
             //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }


    }
}

消费者

public class Consumer {
    public static Runnable runnable = new  Runnable(){
        public void run() {
            //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
            //ip port
            //1.创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("ip");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("user");
            connectionFactory.setPassword("mima");
            connectionFactory.setVirtualHost("/");
            final String queueName = Thread.currentThread().getName();
            Connection connection = null;
            Channel channel = null;
            try {
                //2.创建连接connection
                connection = connectionFactory.newConnection("生产者");
                //3.通过连接获取通道channel
                channel = connection.createChannel();

                /**
                 * @Param1 要接收的队列名
                 * @Param2 是否自动应答
                 */
                Channel finalChannel = channel;
                channel.basicConsume(queueName, true, new DeliverCallback() {
                    public void handle(String consumerTag, Delivery message) throws IOException {
                        System.out.println("消费者 "+queueName+" 收到的信息是:" + new String(message.getBody(), "UTF-8"));
                    }
                }, new CancelCallback() {
                    public void handle(String s) throws IOException {
                        System.out.println("消费者接收消息失败了。。。。");
                    }
                });
                System.out.println("开始接收消息");
                //程序不向下执行
                System.in.read();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                //7.关闭通道
                if (channel != null && channel.isOpen()){
                    try {
                        channel.close();//关闭通道
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                //8.关闭连接
                if (connection != null && connection.isOpen()){
                    try {
                        connection.close(); //关闭连接
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }

    };
    public static void main(String[] args) {
       new Thread(runnable,"codequeue5").start();
       new Thread(runnable,"codequeue6").start();
       new Thread(runnable,"codequeue7").start();

    }
}

轮询分发代码

public class Producer {
    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
      connectionFactory.setHost("ip");
       connectionFactory.setPort(5672);
        connectionFactory.setUsername("user");
        connectionFactory.setPassword("mima");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();

            for (int i = 0; i < 20; i++) {
                //5.准备消息内容
                String message = "HElLO RABBITMQ work模式"+i;

                //6.发送消息给队列
                /**
                 * @Param1:交换机 可以存在没有交换机的队列吗?不可能,虽然没有指定交换机,但一定会存在一个默认的交换机
                 * @Param2:路由key
                 * @Param3: 消息的状态控制
                 * @Param4:消息主题
                 */
                channel.basicPublish("","workqueue1", null,message.getBytes());
                //Thread.sleep(1000);
            }

            System.out.println("消息发送成功咯");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
             //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }


    }
}
public class Consumer1 {

    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
       connectionFactory.setHost("ip");
       connectionFactory.setPort(5672);
        connectionFactory.setUsername("user");
        connectionFactory.setPassword("mima");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();

            /**
             * @Param1 要接收的队列名
             * @Param2 是否自动应答
             */
            channel.basicConsume("workqueue1", true, new DeliverCallback() {
                public void handle(String consumerTag, Delivery message) throws IOException {
                    try {
                        System.out.println("消费者 Consumer1 收到的信息是:" + new String(message.getBody(), "UTF-8"));
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                public void handle(String s) throws IOException {
                    System.out.println("消费者接收消息失败了。。。。");
                }
            });

            System.out.println("Consumer1开始接收消息");
            //程序不向下执行
            System.in.read();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}
package com.doudou.work.circle;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @ClassName Consumer 轮询分发:每个人接收一条数据
 * @Description
 * @Author lihongling
 * @Date 2021/8/26 19:18
 **/
public class Consumer2 {
    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
      connectionFactory.setHost("ip");
       connectionFactory.setPort(5672);
        connectionFactory.setUsername("user");
        connectionFactory.setPassword("mima");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();

            /**
             * @Param1 要接收的队列名
             * @Param2 是否自动应答
             */
            channel.basicConsume("workqueue1", true, new DeliverCallback() {
                public void handle(String consumerTag, Delivery message) throws IOException {
                    try {
                        System.out.println("消费者 Consumer2 收到的信息是:" + new String(message.getBody(), "UTF-8"));
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                public void handle(String s) throws IOException {
                    System.out.println("消费者接收消息失败了。。。。");
                }
            });

            System.out.println("Consumer2开始接收消息");
            //程序不向下执行
            System.in.read();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

公平分发代码
生产者和轮询一样
区别主要在这
RabbitMQ(一)_第20张图片

package com.doudou.work.fair;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @ClassName Consumer 已用图形化界面绑定了
 * @Description
 * @Author lihongling
 * @Date 2021/8/26 19:18
 **/
public class Consumer1 {

    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.20.197");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("cdxx");
        connectionFactory.setPassword("cdxx");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();

            /**
             * @Param1 要接收的队列名
             * @Param2 是否自动应答,false手动应答,公平分发需用false
             */
            final Channel finalChannel = channel;
            finalChannel.basicQos(1);
            finalChannel.basicConsume("workqueue1", false, new DeliverCallback() {
                public void handle(String consumerTag, Delivery delivery) throws IOException {
                    try {
                        System.out.println("消费者 Consumer1 收到的信息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(100);
                        /**
                         * @Param1
                         * @Param2 false,单条消费
                         */
                        finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
                    }catch (Exception e){
                        e.printStackTrace();
                    }

                }
            }, new CancelCallback() {
                public void handle(String s) throws IOException {
                    System.out.println("消费者接收消息失败了。。。。");
                }
            });

            System.out.println("Consumer1开始接收消息");
            //程序不向下执行
            System.in.read();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

public class Consumer2 {
    public static void main(String[] args) {
        //所有的中间件技术都是基于tcp/ip协议基础上构建新型协议规范,只不过rabbitmq遵循的是qmqp协议
        //ip port
        //1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.20.197");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("cdxx");
        connectionFactory.setPassword("cdxx");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            //2.创建连接connection
            connection = connectionFactory.newConnection("生产者");
            //3.通过连接获取通道channel
            channel = connection.createChannel();

            /**
             * @Param1 要接收的队列名
             * @Param2 是否自动应答 false手动应答 公平分发需用false  true就和轮循分发一样了
             */
            final Channel finalChannel = channel;
            finalChannel.basicQos(1);
            finalChannel.basicConsume("workqueue1", false, new DeliverCallback() {
                public void handle(String consumerTag, Delivery delivery) throws IOException {
                    try {
                        System.out.println("消费者 Consumer2 收到的信息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(1000);
                        /**
                         * @Param1
                         * @Param2 false,单条消费
                         */
                        finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                public void handle(String s) throws IOException {
                    System.out.println("消费者接收消息失败了。。。。");
                }
            });

            System.out.println("Consumer2开始接收消息");
            //程序不向下执行
            System.in.read();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //7.关闭通道
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();//关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭连接
            if (connection != null && connection.isOpen()){
                try {
                    connection.close(); //关闭连接
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

界面的一些操作:

RabbitMQ(一)_第21张图片
RabbitMQ(一)_第22张图片
RabbitMQ(一)_第23张图片
绑定交换机:
1.在队列绑定交换机
RabbitMQ(一)_第24张图片

2.在交换机绑定队列
RabbitMQ(一)_第25张图片
RabbitMQ(一)_第26张图片
RabbitMQ(一)_第27张图片

一些报错

发送消息的交换机,不存在

Exception in thread "main" com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=404, reply-text=NOT_FOUND - no exchange 'cdxx-direct-exchange-code' in vhost '/', class-id=60, method-id=40)
	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:502)
	at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:617)
	at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:542)
	at com.rabbitmq.client.impl.ChannelN.close(ChannelN.java:535)
	at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.lambda$close$0(AutorecoveringChannel.java:73)
	at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.executeAndClean(AutorecoveringChannel.java:101)
	at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.close(AutorecoveringChannel.java:73)
	at com.doudou.all.Producer.main(Producer.java:58)
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method(reply-code=404, reply-text=NOT_FOUND - no exchange 'cdxx-direct-exchange-code' in vhost '/', class-id=60, method-id=40)
	at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:517)
	at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:341)
	at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:182)
	at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:114)
	at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:739)
	at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47)
	at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:666)
	at java.lang.Thread.run(Thread.java:748)

你可能感兴趣的:(rabbitmq,rabbitmq,java,分布式)