04. RabbitMQ的几种工作模式

04. RabbitMQ的几种工作模式

🍎姓名:程序员阿红🍎
🍊喜欢:Java编程🍊
🍉重要的事情说三遍!!!🍉
🍓欢迎大家关注哦,互相学习🍓
🍋欢迎大家访问哦,互相学习🍋
🍑欢迎大家收藏哦,互相学习🍑

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🍟🍟🍟RabbitMQ提供了6种消息模型,但是第6种其实是RPC,并不是MQ,因此我们只学习前5种

🍗🍗🍗在线手册:https://www.rabbitmq.com/getstarted.htm

img

5种消息模型,大体分为两类:

  • 1和2属于点对点
  • 3、4、5属于发布订阅模式(一对多)

1. 模式概括

1.1 点对点模式:

P2P(point to point)模式包含三个角色:

  • 消息队列(queue),生产者(sender),消费者(receiver)

  • 每个消息发送到一个特定的队列中,消费者从中获得消息

  • 队列中保留这些消息,直到他们被消费或超时

  • 特点:

    1. 每个消息只有一个消费者,一旦消费,消息就不在队列中了。
    2. 生产者和消费者之间没有依赖性,生产者只管生产消息,消费者只能消费消息。
    1. 接收者成功接收消息之后需向对象应答成功(确认|ACK机制)
  • 如果希望发送的每个消息都会被成功处理,那需要P2P

1.2 发布订阅模式:

publish(Pub)/subscribe(Sub):

  • pub/sub模式包含三个角色:交换机(exchange),发布者(publisher),订阅者
    (subcriber)(没有队列是因为,队列只需要在消费者绑定路由即可)

  • 多个发布者将消息发送交换机,系统将这些消息传递给多个订阅者

  • 特点:

    1. 每个消息可以有多个订阅者
    2. 发布者和订阅者之间在时间上有依赖,对于某个交换机的订阅者,必须创建一个订阅
      后,才能消费发布者的消息
    1. 为了消费消息,订阅者必须保持运行状态;
    2. 如果希望发送的消息被多个消费者处理,可采用本模式

2. 模式分类

2.1点对点模式-简单模式

下面引用官网的一段介绍:
Introduction
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as
a post office: when you put the mail that you want posting in a post box, you can be sure
that Mr. or Ms. Mailperson will eventually deliver the mail to your recipient. In this analogy,
RabbitMQ is a post box, a post office and a postman.
译文:RabbitMQ是一个消息代理:它接收和转发消息。你可以把它想象成一个邮局:当你把你想要
寄的邮件放到一个邮箱里,你可以确定邮递员先生或女士最终会把邮件送到你的收件人那里。在
这个类比中,RabbitMQ是一个邮箱、一个邮局和一个邮递员。
RabbitMQ本身只是接收,存储和转发消息,并不会对信息进行处理!
类似邮局,处理信件的应该是收件人而不是邮局!

  • RabbitMQ本身只是接收,存储和转发消息,并不会对信息进行处理!
  • 类似邮局,处理信件的应该是收件人而不是邮局!

img

生产者代码:

package simplemode;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;

/**
 * @author WeiHong
 * @date 2021 -  09 - 06 16:39
 * 简单模式
 * 消息生产者
 */
public class Sender {
    public static void main(String[] args) throws Exception {
        String msg ="学习rabbitMQ,ACK手动确认!";
        //1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        //2.在连接中创建通道
        Channel channel = connection.createChannel();
        //3.创建消息队列(1、2、3、4、5)
        /**
         * 参数1:队列的名称
         * 参数2:队列中的数据是否持久化
         * 参数3:是否排外(是否支持扩展,当前队列只能自己用,不能给别人用)
         * 参数4:是否自动删除(当队列连接数为0时,队列会销毁,不管队列是否存有保存数据)
         * 参数5:队列参数(没有参数为null)
         */
        channel.queueDeclare("queue1",false,false,false,null);
        //4.向指定的队列发送消息
        /**
         * 参数1:交换机名称,当前为简单模式,也就是p2p模式,没有交换机,所以名称为“”;
         * 参数2:目标队列的名称
         * 参数3:设置消息的属性(没有属性则为null)
         * 参数4:消息的内容(只接收字节数组)
         */
        channel.basicPublish("","queue1",null,msg.getBytes());
        System.out.println("发送:"+msg);
        //5.释放资源
        channel.close();
        connection.close();

    }
}

启动生产者,即可前往管理端查看队列中的信息,会有一条信息没有处理和确认

img

消费者代码:

package simplemode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 06 17:00
 * 消息接收者
 */
public class Recer {
    public static void main(String[] args)throws Exception{
        // 1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        // 2.在连接中创建通道(信道)
        Channel channel =  connection.createChannel();
        // 3.从信道中获得信息
        DefaultConsumer consumer =  new DefaultConsumer(channel){
            @Override//交付处理(收件人信息,信封(包裹上的快递标签),协议的配置,消息)
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //body就是从队列中获取的消息
                String s= new String(body);
                System.out.println("接收="+s);
            }
         };
        // 4.监听队列 true:自动消息确认
        channel.basicConsume("queue1",true,consumer);


    }
}

启动消费者,前往管理端查看队列中的信息,所有信息都已经处理和确认,显示0

img

消息确认机制ACK:

  • 通过刚才的案例可以看出,消息一旦被消费,消息就会立刻从队列中移除
  • RabbitMQ如何得知消息被消费者接收?
    1. 如果消费者接收消息后,还没执行操作就抛异常宕机导致消费失败,但是RabbitMQ无从得知,这样消息就丢失了
    2. 因此,RabbitMQ有一个ACK机制,当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收
    1. ACK:(Acknowledge character)即是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误我们在使用http请求时,http的状态码200就是告诉我们服务器执行成功
    2. 整个过程就想快递员将包裹送到你手里,并且需要你的签字,并拍照回执
    1. 不过这种回执ACK分为两种情况:
      1. 自动ACK:消息接收后,消费者立刻自动发送ACK(快递放在快递柜)
      2. 手动ACK:消息接收后,不会发送ACK,需要手动调用(快递必须本人签收)
    1. 两种情况如何选择,需要看消息的重要性:
      1. 如果消息不太重要,丢失也没有影响,自动ACK会比较方便
      2. 如果消息非常重要,最好消费完成手动ACK,如果自动ACK消费后,RabbitMQ就会把
      1. 消息从队列中删除,如果此时消费者抛异常宕机,那么消息就永久丢失了
  • 修改手动消息确认
        // 4.监听队列 false:手动消息确认
        channel.basicConsume("queue1", false,consumer);
  • 结果如下

img

确认ACK代码如下:

package simplemode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @BelongsProject: lagou-rabbitmq
 * @Author: GuoAn.Sun
 * @CreateTime: 2020-08-10 15:08
 * @Description: 消息接收者,加入ACK确认机制
 */
public class RecerByACK {
    public static void main(String[] args) throws  Exception {
        // 1.获得连接
        Connection connection = ConnectionUtil.getConnection();
        // 2.获得通道(信道)
        final Channel channel = connection.createChannel();
        // 3.从信道中获得消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override //交付处理(收件人信息,包裹上的快递标签,协议的配置,消息)
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                // body就是从队列中获取的消息
                String s = new String(body);
                System.out.println("接收 = " + s);
                // 手动确认(收件人信息,是否同时确认多个消息)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        // 4.监听队列 false:手动消息确认
        channel.basicConsume("queue1", false,consumer);
    }
}

ACK确认模式:

*// 4.监听队列 false:手动消息确认,必须为手动消息确认,ACK确认机制才生效。
*channel.basicConsume(“queue1”,fasle,consumer);

img

没执行channel.basicAck(envelope.getDeliveryTag(),false);该语句时消息是未被确认的。

2.2 工作队列模式

img

  • 之前我们学习的简单模式,一个消费者来处理消息,如果生产者生产消息过快过多,而消费者的能力有限,就会产生消息在队列中堆积(生活中的滞销)

  • 一个烧烤师傅,一次烤50支羊肉串,就一个人吃的话,烤好的肉串会越来越多,怎么处理?

  • 多招揽客人进行消费即可。当我们运行许多消费者程序时,消息队列中的任务会被众多消费者共享,但其中某一个消息只会被一个消费者获取(100支肉串20个人吃,但是其中的某支肉串只能被一个人吃)

生产者代码:

package workmode;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;

/**
 * @author WeiHong
 * @date 2021 -  09 - 06 19:23
 * 工作模型
 */
public class Sender {
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("work_queue",false,false,false,null);
        for(int i = 1;i<=100;i++) {
            String msg = "羊肉串 --> " + i;
            channel.basicPublish("", "work_queue", null, msg.getBytes());
            System.out.println("新鲜出炉:" + msg);
        }
        channel.close();
        connection.close();
    }
}

消费者1代码:

package workmode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 06 21:22
 */
public class Recer1 {
    static  int i =1;
    public static void main(String[] args)throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //声明队列、双重含义
        // 如果队列不存在,就创建;如果队列存在,则获取
        channel.queueDeclare("work_queue",false,false,false,null);
        channel.basicQos(1);//系统不会给消费者发送超过1个消息以上,只有确认ACK后,才回继续发下一个消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //body就是从队列中获取的消息
                String s= new String(body);
                System.out.println("【顾客1】吃掉 " + s+" ! 总共吃【"+i+++"】串!"+"信封收件人:"+envelope.getDeliveryTag());
                //模拟网络延迟
                try {
                    Thread.sleep(200);
                }catch (Exception e){
                    e.printStackTrace();
                }
                //手动确认(收件人信息,是否确认多个消息)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        // 4.监听队列 false:手动消息确认
        channel.basicConsume("work_queue", false,consumer);
    }
}

消费者2代码:

package workmode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @BelongsProject: lagou-rabbitmq
 * @Author: GuoAn.Sun
 * @CreateTime: 2020-08-10 15:08
 * @Description: 消息接收者,加入ACK确认机制
 */
public class Recer2 {
    static  int i =1;
    public static void main(String[] args) throws  Exception {
        // 1.获得连接
        Connection connection = ConnectionUtil.getConnection();
        // 2.获得通道(信道)
        final Channel channel = connection.createChannel();
        channel.queueDeclare("work_queue",false,false,false,null);
        channel.basicQos(1);
        // 3.从信道中获得消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override //交付处理(收件人信息,包裹上的快递标签,协议的配置,消息)
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String s = new String(body);
                System.out.println("【顾客2】吃掉 " + s+" ! 总共吃【"+i+++"】串!");
                // 模拟网络延迟
                try{
                    Thread.sleep(900);
                }catch (Exception e){

                }
                // 手动确认(收件人信息,是否同时确认多个消息)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        // 4.监听队列 false:手动消息确认
        channel.basicConsume("work_queue", false,consumer);
    }
}

结果:

生产者产生100个羊肉串

img

消费者1消费了81条消息

img

消费者2消费了19条消息

img

channel.basicQos:系统不会给消费者发送超过1个消息以上,只有确认ACK后,才回继续发下一个消息

  1. 不加channel.basicQos(1);
  • 不加channel.basicQos(1),消费者是相互竞争的关系,但是会把消息进行平分。
  • 例:1个生产者;2个消费者。生产者发送100条信息,消费者尽管消费速度不一样,这两个消费者还是会将这100条数据各分50条。
  1. 加channel.basicQos(1);
  • 遵循能者多劳机制,消费者是相互竞争的关系,但是不会把消息进行平分。消费速度快的会继续消费信息,直至所有消息被消费完。

能者多劳必须要配合手动的ACK机制才生效:

  • 面试题:避免消息堆积?
  1. workqueue,多个消费者监听同一个队列

  2. 接收到消息后,通过线程池,异步消费

2.3 发布订阅模式

发布-订阅

在上一篇教程中,我们创建了一个工作队列。工作队列背后的假设是,每个任务都被准确地交

付给一个工作者。在这一部分中,我们将做一些完全不同的事情——将消息传递给多个消费者。

此模式称为“发布/订阅”。

为了演示这个模式,我们将构建一个简单的日志记录系统。它将由两个程序组成——第一个将

发送日志消息,第二个将接收和打印它们。

在我们的日志系统中,接收程序的每一个正在运行的副本都将获得消息。这样我们就可以运行

一个接收器并将日志指向磁盘;与此同时,我们可以运行另一个接收器并在屏幕上看到日志。

基本上,发布的日志消息将广播到所有接收方

生活中的案例:就是玩抖音快手,众多粉丝关注一个视频主,视频主发布视频,所有粉丝都可以得到视

频通知

img

上图中,X就是视频主,红色的队列就是粉丝。binding是绑定的意思(关注)

P生产者发送信息给X路由,X将信息转发给绑定X的队列

img

X队列将信息通过信道发送给消费者,从而进行消费

整个过程,必须先创建路由

路由在生产者程序中创建

因为路由没有存储消息的能力,当生产者将信息发送给路由后,消费者还没有运行,所以没

有队列,路由并不知道将信息发送给谁

运行程序的顺序:

1.先运行生产者sender;创建路由

2在运行消费者Recer1、Recer2。

生产者代码

package publishSubscribeMode;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;

/**
 * @author WeiHong
 * @date 2021 -  09 - 07 14:11
 * 发布订阅模式
 */
public class Sender {
    public static void main(String[] args) throws Exception {
        // 1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        // 2.在连接中创建信道
        Channel channel = connection.createChannel();
        // 3.声明路由(4种路由)
        // fanout:不处理路由键(只需要将队列绑定到路由上,发送到路由的消息都会被转发到与该路由绑定的所有队列上)
        channel.exchangeDeclare("test_exchange_fanout","fanout");
        // 4.发布消息
        String msg = "这是发布订阅模式的fanout路由模型的消息";
        channel.basicPublish("test_exchange_fanout","",null,msg.getBytes());
        System.out.println("发送:"+msg);
        // 5.释放资源
        channel.close();
        connection.close();
    }
}

消费者1代码

package publishSubscribeMode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 07 15:29
 * 发布订阅模式-消费者1
 */
public class Recer1 {
    public static void main(String[] args)throws Exception{
        // 1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        // 2.在链接中创建信道
        Channel channel = connection.createChannel();
        //2.1声明队列
        channel.queueDeclare("test_exchange_fanout_queue1",false,false,false,null);
        //2.2绑定路由
        channel.queueBind("test_exchange_fanout_queue1","test_exchange_fanout","");
        // 3.定义内部类重写方法接收消息\从信道中获得信息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String s = new String(body);
                System.out.println("[消费者1] = "+ s);
            }
        };
        // 4.监听队列 true:自动消息确认
        channel.basicConsume("test_exchange_fanout_queue1",true,consumer);

    }
}

消费者2代码

package publishSubscribeMode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 07 15:29
 * 发布订阅模式-消费者2
 */
public class Recer2 {
    public static void main(String[] args)throws Exception{
        // 1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        // 2.在链接中创建信道
        Channel channel = connection.createChannel();
        //2.1声明队列
        channel.queueDeclare("test_exchange_fanout_queue2",false,false,false,null);
        //2.2绑定路由
        channel.queueBind("test_exchange_fanout_queue2","test_exchange_fanout","");
        // 3.定义内部类重写方法接收消息\从信道中获得信息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String s = new String(body);
                System.out.println("[消费者2] = "+ s);
            }
        };
        // 4.监听队列 true:自动消息确认
        channel.basicConsume("test_exchange_fanout_queue2",true,consumer);

    }
}

2.4 路由模式

img

  • 路由会根据类型进行定向分发消息给不同的队列,如图所示

生产者代码

package routerMode;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;

/**
 * @author WeiHong
 * @date 2021 -  09 - 09 14:38
 * 路由模式-生产者
 * 路由会根据类型进行定向分发消息给不同的队列
 */
public class Sender {
    public static void main(String[] args) throws Exception {
        //1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        //2.在连接中创建信道
        Channel channel = connection.createChannel();
        //3.路由声明(路由名,路由类型)
        //direct:会根据路由键进行定向分发消息
        channel.exchangeDeclare("router_exchange_direct","direct");
        //4.向队列发送信息
        for(int i = 0; i < 10 ; i++){
            String msg = "查询的数据第"+i+"条消息!";
            //selecte:此时该参数为路由键不是队列名
            channel.basicPublish("router_exchange_direct","selecte",null,msg.getBytes());
            System.out.println("生产者发送="+msg);
        }
        //5.释放连接
        channel.close();
        connection.close();
    }
}

消费者1代码

package routerMode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 09 15:05
 */
public class Recer1 {
    public static void main(String[] args) throws Exception {
        //1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        //2.在链接中创建信道
        Channel channel = connection.createChannel();
        //2.1声明队列
        channel.queueDeclare("exchange_direct_queue1",false,false,false,null);
        //2.2绑定路由(如果路由键的类型是插入、修改绑定到这个队列1上)
        channel.queueBind("exchange_direct_queue1","router_exchange_direct","insert");
        channel.queueBind("exchange_direct_queue1","router_exchange_direct","update");
        //3.定义内部类获取信息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String res = new String(body);
                System.out.println("【消费者1】="+ res);
            }

        };
        //4.监听队列 true:自动消息确认
        channel.basicConsume("exchange_direct_queue1",true,consumer);

    }
}

消费者2代码

package routerMode;

import com.rabbitmq.client.*;
import util.ConnectionUtil;
import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 09 15:05
 */
public class Recer2 {
    public static void main(String[] args) throws Exception {
        //1.获取连接
        Connection connection = ConnectionUtil.getConnection();
        //2.在链接中创建信道
        Channel channel = connection.createChannel();
        //2.1声明队列
        channel.queueDeclare("exchange_direct_queue2",false,false,false,null);
        //2.2绑定路由(如果路由键的类型是查询绑定到这个队列2上)
        channel.queueBind("exchange_direct_queue2","router_exchange_direct","selecte");
        //3.定义内部类获取信息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String res = new String(body);
                System.out.println("【消费者2】="+ res);
            }

        };
        //4.监听队列 true:自动消息确认
        channel.basicConsume("exchange_direct_queue2",true,consumer);

    }
}

运行顺序

  1. 记住运行程序的顺序,先运行一次sender(创建路由器),

  2. 有了路由器之后,在创建两个Recer1和Recer2,进行队列绑定

  3. 再次运行sender,发出消息

消费者接收信息由路由键确定,当生产者发送信息到路由上时,因为指定了路由键,所以消息会被指派到对应的消息队列里。消费者通过队列绑定路由接收到不同路由键的消息。

//selecte:此时该参数为路由键不是队列名 
channel.basicPublish("router_exchange_direct","selecte",null,msg.getBytes());
 //2.2绑定路由(如果路由键的类型是查询绑定到这个队列2上)
 channel.queueBind("exchange_direct_queue2","router_exchange_direct","selecte");

2.5 通配符模式

img

和路由模式90%是一样的。

唯独的区别就是路由键支持模糊匹配

匹配符号

*:只能匹配一个词(正好一个词,多一个不行,少一个也不行)

#:匹配0个或更多个词

看一下官网案例:

Q1绑定了路由键 .orange. Q2绑定了路由键 ..rabbit 和 lazy.#

下面生产者的消息会被发送给哪个队列?

quick.orange.rabbit 	# Q1 Q2
lazy.orange.elephant 	# Q1 Q2
quick.orange.fox 		# Q1
lazy.brown.fox 			# Q2
lazy.pink.rabbit 		# Q2
quick.brown.fox			# 无
orange 					# 无
quick.orange.male.rabbit # 无

生产者代码

package topic;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import util.ConnectionUtil;

/**
 * @author WeiHong
 * @date 2021 -  09 - 14 14:03
 */
public class Sender {
    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明路由(路由名,路由类型)
        // topic:模糊匹配的定向分发
        channel.exchangeDeclare("test_exchange_topic", "topic");
        String msg = "商品降价";
        channel.basicPublish("test_exchange_topic", "produce.price.test", null,
                msg.getBytes());
        System.out.println("[用户系统]:" + msg);
        channel.close();
        connection.close();
    }
}

消费者1代码

package topic;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 14 14:10
 */
public class Recer1 {
    public static void main(String[] args) throws Exception {
        //1.建立连接
        Connection connection = ConnectionUtil.getConnection();
        //2.在链接中建立信道
        Channel channel = connection.createChannel();
        //3.声明队列
        channel.queueDeclare("test_exchange_topic_queue1",false,false,false,null);
        //4.绑定路由
        channel.queueBind("test_exchange_topic_queue1","test_exchange_topic","user.*");
        //5.定义内部类接收消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body);
                System.out.println("[消费者1]接收到的信息:"+msg);
            }

        };
        //6.监听队列 true:消息自动确认
        channel.basicConsume("test_exchange_topic_queue1", true,consumer);
    }
}

消费者2代码

package topic;

import com.rabbitmq.client.*;
import util.ConnectionUtil;

import java.io.IOException;

/**
 * @author WeiHong
 * @date 2021 -  09 - 14 14:10
 */
public class Recer2 {
    public static void main(String[] args) throws Exception {
        //1.建立连接
        Connection connection = ConnectionUtil.getConnection();
        //2.在链接中建立信道
        Channel channel = connection.createChannel();
        //3.声明队列
        channel.queueDeclare("test_exchange_topic_queue2",false,false,false,null);
        //4.绑定路由
        channel.queueBind("test_exchange_topic_queue2","test_exchange_topic","produce.#");
        //5.定义内部类接收消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body);
                System.out.println("[消费者2]接收到的信息:"+msg);
            }

        };
        //6.监听队列 true:消息自动确认
        channel.basicConsume("test_exchange_topic_queue2", true,consumer);
    }
}

几种常见路由件说明:

  1. 发布/订阅模式

*// fanout:不处理路由键(只需要将队列绑定到路由上,发送到路由的消息都会被转发到与该路由绑定的所有队列上)
*channel.exchangeDeclare(“test_exchange_fanout”,“fanout”);

  1. 路由模式

*//direct:会根据路由键进行定向分发消息
*channel.exchangeDeclare(“router_exchange_direct”,“direct”);

  1. 通配符模式

*// topic:模糊匹配的定向分发
*channel.exchangeDeclare(“test_exchange_topic”, “topic”);

🍎姓名:程序员阿红🍎
🍊喜欢:Java编程🍊
🍉重要的事情说三遍!!!🍉
🍓欢迎大家关注哦,互相学习🍓
🍋欢迎大家访问哦,互相学习🍋
🍑欢迎大家收藏哦,互相学习🍑
✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员阿红

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值