RabbitMQ笔记

222 阅读5分钟

RabbitMQ核心组成部分

kuangstudy62a1f9e3-027d-408a-8fb4-a176bd184d23.png

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



simple模式

image.png

get message一定要选择Nack 如果选择ack会当成真实的消费 把队列的这条消息消费掉

image.png



RabbitMQ的运行流程

kuangstudy2704cee9-3595-45de-892d-ee658e848806.png



fanout模式

相比simple模式多了exchange image.png



创建一个交换机fanout-demo type选择fanout

image.png

在bindings中绑定三个queue

image.png

通过交换机向三个queue发送消息

image.png

最后三条队列收到消息

image.png



routing模式

相比fanout模式多了一层路由key fanout中指定路由key没有意义 image.png

创建一个direct交换机

image.png

绑定三个队列 并给他们路由key

image.png

给路由key为vx的队列发送消息

image.png

可以看到只有2和3收到了消息

image.png



topic模式

在routing模式上新增了模糊匹配路由key image.png

创建一个topic exchange type选择 topic image.png

绑定队列

image.png

com.# #代表占位0个,1个或多个或多级 例如com   com.xxxxxxx       com.xxx.xxx
com.*代表占位至少一个并且只有一级 例如com.xxx

例如发送的路由key为com.course.order
那么队列1,2,3都能收到消息 切记#后面可以为空

image.png

image.png

如果发送的是com.course.order.user
那么只有队列1,3能收到消息 因为*只能有1级并且不能为空 所以队列2,4收不到消息 image.png

image.png



headers模式

type选择headers image.png

给队列1和2绑定并给予参数

image.png

给参数x=1的队列发送消息 image.png

队列1收到消息

image.png

代码模板

Producer

public class Producer {
    public static void main(String[] args) {
        //1.创建连接工厂
        ConnectionFactory connectionFactory=new ConnectionFactory();
        //2.设置连接属性
        connectionFactory.setHost("8.130.18.190");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");

        Connection connection=null;
        Channel channel=null;
        try{
            //3.从连接工厂中获取连接
            connection=connectionFactory.newConnection("生产者");
            //4.从连接中获取通道channel
            channel=connection.createChannel();
            //5.准备发送消息的内容
            String message="ALL DIRECT";
            String exchangeName="direct-all";  //声明一个不存在的交换机
            String exchangeType="direct";

            channel.exchangeDeclare(exchangeName,exchangeType,true);//创建一个交换机
            //声明队列
            /**
             *  @params1: queue 队列的名称
             *  @params2: durable 队列是否持久化
             *  @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
             *  @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
             *  @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
             */
            channel.queueDeclare("queue04",true,false,false,null);
            channel.queueDeclare("queue05",true,false,false,null);
            channel.queueDeclare("queue06",true,false,false,null);
            //绑定关系
            channel.queueBind("queue04",exchangeName,"qq");
            channel.queueBind("queue05",exchangeName,"tieba");
            channel.queueBind("queue06",exchangeName,"qq");

            // 6: 发送消息给中间件rabbitmq-server
            // @params1: 交换机exchange
            // @params2: 队列名称/routingkey
            // @params3: 属性配置
            // @params4: 发送消息的内容

            String routingkey="qq";
            channel.basicPublish(exchangeName,routingkey,null,message.getBytes());
            System.out.println("发送成功");


        }catch (Exception e){
            e.printStackTrace();
            System.out.println("发送出现错误");
        }finally {
            // 7: 释放连接关闭通道
            if (channel!=null&&channel.isOpen()){
                try{
                    channel.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
                try{
                    connection.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

Consumer

public class Consumer {
    private static Runnable runnable = () -> {
        //1.创建连接工厂
        ConnectionFactory connectionFactory=new ConnectionFactory();
        //2.设置连接属性
        connectionFactory.setHost("8.130.18.190");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        //获取队列的名称
        final String queueName = Thread.currentThread().getName();
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 5: 申明队列queue存储消息
            /*
             *  如果队列不存在,则会创建
             *  Rabbitmq不允许创建两个相同的队列名称,否则会报错。
             *
             *  @params1: queue 队列的名称
             *  @params2: durable 队列是否持久化
             *  @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
             *  @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
             *  @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
             * */
            // 这里如果queue已经被创建过一次了,可以不需要定义
            //channel.queueDeclare("queue1", false, false, false, null);
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;
            finalChannel.basicConsume(queueName, true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println(queueName + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });
            System.out.println(queueName + ":开始接受消息");
            System.in.read();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    };
    public static void main(String[] args) {
        // 启动三个线程去执行
        new Thread(runnable, "queue01").start();
        new Thread(runnable, "queue02").start();
        new Thread(runnable, "queue03").start();
        new Thread(runnable, "queue04").start();
        new Thread(runnable, "queue05").start();
        new Thread(runnable, "queue06").start();
    }
}

Work轮询模式

主要有两种模式:
1、轮询模式的分发:一个消费者一条,按均分配;
2、公平分发:根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配;

默认是轮询分发

#改用公平分发
finalChannel.basicQos(1);  //表示每次取1条消息 建议不要设置太大 几十即可
finalChannel.basicConsume("queue01", false, new DeliverCallback() //@params2:autoAck改为false

#改为手动应答
finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
 

Linux中启动Docker容器报错:Error response from daemon: driver failed programming external connectivity

请使用以下命令重启docker

sudo systemctl restart docker