RabbitMQ七种工作模式介绍:

1.Simple(简单模式):

P:生产者,也就是要发送的程序。

C:消费者,消息的接受者。

Queue:消息队列,图中黄色的部分,类似一个邮箱,P可以往里面投敌消息,消费者可以从其中取出消息。

特点:一个生产者P,一个消费者C,消息只能被消费一次,也被称为点对点模式。

2.Work Queue(工作队列):

一个生产者P,多个消费者C,在多个消息的情况下,Work Queue会将消息分派给不同的消费者,也就是说每个消费者收到的消息都不同。

特点:消息不会重复,分配给不同的消费者。

交换机有关的工作模式:

在订阅模型中,多了一个交换机角色,过程略有办法。

概念介绍:

交换机(exchange)

作用:生产者将消息发送到Exchange中,然后交换机按照一定规则路由到一个或多个队列中。

RabbitMQ交换机有四种类型:fanout,direct,topic,headers,不同类型有不同的路由策略。

Fanout:广播,将所有消息交给所绑定到交换机的队列(Publish/Subscribe模式)

Direct:定向,把消息定向交给指定的routingKey的队列(Routing模式)

Topic:通配符,把消息交给符合Routing pattern(路由模式)的队列(Topics模式)

headers:headers类型的交换器不依赖于路由键的匹配规则来路由消息,而且也不实用,基本不会用到。

3.Publish/Subscribe(发布/订阅):

一个生产者P,多个消费者,这时就多了一个交换机的角色,每个消费者都能收到相同的消息。生产者发送一条消息,经过交换机转换到多个不同的队列,多个队列有多个不同的消费者。

适合场景:消息需要被多个消费者同时接收的场景,如:实时通知或者广播消息。

4.Routing(路由模式):

路由模式是发布订阅模式的变更,比发布订阅模式基础上,加了一个路由key,发布订阅模式是无条件将所有消息分发给所有消费者,路由模式是Exchange根据RoutingKey的规则,将数据进行匹配后发送给对应的消费者队列。

X是交换机,然后根据不同的RoutingKey(a,b,c)发送到对应的队列中。

5.Topics(通配符模式):

通配符模式是路由模式的升级,在RoutingKey的基础上,增加了通配符的功能,使之更加灵活。

Topic和Routing的基本原理相同,但是匹配的规则不同,Routing是相等匹配,而topics模式是通配符匹配。

*号匹配一个且仅一个单词。

#号匹配0个或者多个单词。

6.RPC(RPC通信):

在RPC通信过程中,没有生产者和消费者,比较像咱们RPC远程调用,大概通过两个队列实现了一个可回调过程。

1.客户端发送一个消息到指定的队列,并且在消息属性中设置ReplyTo字段,这个字段指定了一个回到队列,用于接受服务器的响应。

2.服务器就收到消息后,处理请求并响应消息到ReplyTo指定的回调队列中。

3.客户端在回调队列上等待响应消息,一旦接收到响应,客户端会检查消息的correlationId属性,确保是不是自己期望的响应。

7.Publisher Confirm(发布确认):

Publisher Comfirm模式是RabbitMQ提供的一种确保消息可靠发送到RabbitMQ服务器的机制,这种模式下,生产者发送了消息后,可以等待RabbitMQ服务器的确认,以确保消息已经被服务器接受并处理。

1.生产者将channel设置为Confirm模式(通过调用channel.confirmSelect()完成)后,发布每一条消息都会获得一个唯一ID,生产者可以将这些序列号关联起来,追踪消息的状态。

2.当消息被RabbitMQ服务器接收并处理后,服务器会异步的向生产者发送一个确认(ACK)给生产者(包含唯一ID),表示消息已经送达。

工作队列的代码:

1.引入依赖:

java 复制代码
 <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.25.0</version>
 </dependency>

2.编写生产者代码:

java 复制代码
public static void main(String[] args) throws IOException, TimeoutException {
        //创建channel通道
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection=factory.newConnection();
        Channel channel=connection.createChannel();
        //声明队列,如果没有这样的队列,就会自动生成一个,如果有则不生成。
        channel.queueDeclare("hello",true, false, false, null);
        //发送消息
        for (int i = 0; i < 10; i++) {
            String message = "Hello RabbitMQ!";
            //发送消息,默认交换机是""
            channel.basicPublish("","hello",null,message.getBytes());
        }
        //释放资源
        channel.close();
        connection.close();
    }

3.编写消费者代码:

java 复制代码
 public static void main(String[] args) throws IOException, TimeoutException {
        //创建channel通道
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");//默认值localhost
        connectionFactory.setPort(5672);//默认值是5672
        connectionFactory.setUsername("guest");//用户名,默认guest
        connectionFactory.setPassword("guest");//密码,默认guest
        connectionFactory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("hello", true, false, false, null);
        //接收消息,并消费
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到消息"+new String(body));
            }
        };
        channel.basicConsume("hello",true,consumer);
    }

运行结果:

Publish/Subscribe(发布/订阅)代码:

在发布订阅模式中,多了一个Exchange交换机。同时还有将交换机和队列进行关系绑定,以便路由到对应的队列中,给消费者消费。

1.引入依赖:

java 复制代码
 <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.25.0</version>
 </dependency>

2.生产者代码:

java 复制代码
public static void main(String[] args) throws IOException, TimeoutException {
        //建立channel通道
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //声明交换机,因为这里是发布/订阅模式,所以type是BuiltinExchangeType.FANOUT
        channel.exchangeDeclare("fanout.exchange",BuiltinExchangeType.FANOUT,true,false,false,null);
        //声明队列,这里声明两个队列
        channel.queueDeclare("fanout.queue1",true,false,false,null);
        channel.queueDeclare("fanout.queue2",true,false,false,null);
        //绑定交换机和队列,后面的routinKey是匹配规则,这里的发布订阅模式所以是""
        channel.queueBind("fanout.queue1", "fanout.exchange","");
        channel.queueBind("fanout.queue2", "fanout.exchange","");
        //发送消息
        String message = "Hello fanout!!";
        channel.basicPublish("fanout.exchange","", null, message.getBytes());
        //资源释放
        channel.close();
        connection.close();
    }

3.消费者代码:

这里有两个消费者:

消费者1代码:

java 复制代码
  public static void main(String[] args) throws IOException, TimeoutException {
        //建立channel通道
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("fanout.queue1", true, false, false, null);
        //消费队列
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到消息"+new String(body));
            }
        };
        channel.basicConsume("fanout.queue1",true,consumer);
    }

消费者2代码:

java 复制代码
public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("fanout.queue2", true, false, false, null);
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到消息"+new String(body));
            }
        };
        channel.basicConsume("fanout.queue2",true,consumer);
    }

运行结果:

消费者1:

消费者2:

可以看出发布订阅模式中,每个消费者都收到了相同的消息。

Routing(路由模式):

路由模式不同于上面的发布/订阅模式,路由模式的匹配规则不是任意绑定了,而是要指定一个BindingKey的一种。

而且生产者在像Exchange发送消息时,也需要指定消息的RoutingKey。

Exchange不再把消息交给每个绑定的key,而是根据消息的RoutingKey进行判断,只有队列绑定时的BindKey和发送消息的Routingkey完全一致时,才会收到消息。

接下来看代码实现:

1.引入依赖:
java 复制代码
 <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.25.0</version>
 </dependency>
2.编写生产者代码:

这时交换机类型是DIRECT。

java 复制代码
 public static void main(String[] args) throws IOException, TimeoutException {
        //建立channel通道
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //创建交换机
        channel.exchangeDeclare("direct.exchange", BuiltinExchangeType.DIRECT,true,false,false,null);
        //声明队列
        channel.queueDeclare("direct.queue1", true, false, false, null);
        channel.queueDeclare("direct.queue2", true, false, false, null);
        //绑定队列和交换机的关系
        channel.queueBind("direct.queue1", "direct.exchange", "a");
        channel.queueBind("direct.queue2", "direct.exchange", "a");
        channel.queueBind("direct.queue2", "direct.exchange", "b");
        channel.queueBind("direct.queue2", "direct.exchange", "c");
        //发送消息
        String msga="hello a!";
        String msgb="hello b!";
        String msgc="hello c!";
        channel.basicPublish("direct.exchange", "a", null, msga.getBytes());
        channel.basicPublish("direct.exchange", "b", null, msgb.getBytes());
        channel.basicPublish("direct.exchange", "c", null, msgc.getBytes());
        System.out.println("消息发送成功!!!");
        //释放资源
        channel.close();
        connection.close();
    }
3.编写消费者代码:

消费者1代码:

java 复制代码
 public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //接收消息并消费
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到消息"+new String(body));
            }
        };
        channel.basicConsume("direct.queue1",true,consumer);
    }

消费者2代码:

java 复制代码
 public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //接收消息并消费
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到消息"+new String(body));
            }
        };
        channel.basicConsume("direct.queue2",true,consumer);
    }
运行结果:

消费者1:

消费者2:

从运行结果可以看出Routing模式的规则。因为发送a消息时,只发送给了对应的消费者1,但发送a,b,c时,发送给了消费者2,因为他们二者和交换机的绑定规则不一样。

Topics(通配符模式):

这里的通配符模式,交换机类型是TOPIC

topic类型的交换机在匹配规则上进行了扩展,BindKey支持通配符匹配,direct类型的交换机的规则是Bindingkey和Routingkey完全匹配。

代码和上面的Routing模式一样,我就只写区别代码,也就是交换机和队列绑定的代码。

java 复制代码
  //声明交换机和队列
        channel.exchangeDeclare("topic.exchange", BuiltinExchangeType.TOPIC,true);
        channel.queueDeclare("topic.queue1", true, false, false, null);
        channel.queueDeclare("topic.queue2", true, false, false, null);
        //绑定交换机和队列,这里的绑定规则使用通配符绑定,*表示匹配一个且仅一个单词,#表示匹配0个或多个单词
        channel.queueBind("topic.queue1", "topic.exchange", "*.a.*");
        channel.queueBind("topic.queue2", "topic.exchange", "*.*.b");
        channel.queueBind("topic.queue2", "topic.exchange", "c.#");
        //发送消息,此时,也要注明要将消息发送哪一个交换机,并且写明BingingKey,后续根据对应的BingingKey,匹配到对应的队列中
        String message = "Hello A!";
        channel.basicPublish("topic.exchange", "e.a.f", null, message.getBytes());
        String message2 = "Hello B!";
        channel.basicPublish("topic.exchange", "ef.a.b", null, message2.getBytes());
        String message3 = "Hello C!";
        channel.basicPublish("topic.exchange", "c.ef.b", null, message3.getBytes());

RPC(RPC通信):

RPC(Remote Procedure Call),即远程过程调用,它是一种通过网络从远程计算机上请求服务,而不需要了解底层网络的技术,类是与Http远程调用。

RabbitMQ实现RPC通信的过程,大概是通过两个队列实现一个可回调的过程。

大致流程如下:

1.客户端发送一个消息到一个指定的队列,并在消息属性中设置replyTo字段,这个字段指定了一个回调队列,服务端处理后,会把响应结果发送到这个队里。

2.服务端接收到请求后,处理请求并发送响应消息到replyTo指定的回调队列。

3.客户端在回调队列上等待响应的消息,一旦有消息响应,客户端就会检查消息的correlationId属性,以确保是自己想要的响应。

PRC的代码实现:

1.引入依赖:

java 复制代码
 <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.25.0</version>
 </dependency>

2.客户端代码:

java 复制代码
 public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare("rpc.queue", true, false, false, null);
        //定义回调队列
        channel.queueDeclare("rpc.response.queue", true, false, false, null);
        String message = "Hello RabbitMQ!";
        //本词请求的唯一标志
        String correlationId = UUID.randomUUID().toString();
        //生成发送消息的属性
        AMQP.BasicProperties props=new AMQP.BasicProperties().builder()
                        .correlationId(correlationId)
                .replyTo("rpc.response.queue")
                .build();
        //发送消息
        channel.basicPublish("", "rpc.queue", props,message.getBytes());

        //阻塞队列,用于储存回调结果
        final BlockingQueue<String> queue = new LinkedBlockingQueue<String>(1);
        //接收服务器的响应
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("接收到回调消息" + new String(body));
                //进行标识判断,放到阻塞队列中
                if(correlationId.equals(properties.getCorrelationId())){
                     queue.offer(new String(body));
                }
            }
        };
        channel.basicConsume("rpc.response.queue",true,consumer);
        //获取回调结果
                String result=queue.take();
        System.out.println("RPC响应结果+"+result);
    }

3.服务端代码:

java 复制代码
public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//默认值localhost
        factory.setPort(5672);//默认值是5672
        factory.setUsername("guest");//用户名,默认guest
        factory.setPassword("guest");//密码,默认guest
        factory.setVirtualHost("Virtual host");//虚拟机的名称
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //设置同时只能获取一个消息
        channel.basicQos(1);
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
                //处理请求,并生成返回
                System.out.println("接收到请求!!"+new String(body));
                AMQP.BasicProperties props=new AMQP.BasicProperties().builder()
                        .correlationId(properties.getCorrelationId())
                        .build();
                //回复消息,通知已经收到的请求
                channel.basicPublish("", "rpc.response.queue", props, body);
                //对消息进行应答
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume("rpc.queue",false,consumer);
    }

运行结果:

客户端:

服务端:

可以看出,客户端给服务端成功发送了请求,并成功处理后,服务端还给客户端进行响应请求。

还有一种发布确认模式,在后面的文章会续上。

相关推荐
用户83071968408221 小时前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq
初次攀爬者2 天前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者4 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧5 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖5 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农5 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者5 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端
业精于勤_荒于稀5 天前
物流订单系统99.99%可用性全链路容灾体系落地操作手册
分布式
Ronin3055 天前
信道管理模块和异步线程模块
开发语言·c++·rabbitmq·异步线程·信道管理
Asher05095 天前
Hadoop核心技术与实战指南
大数据·hadoop·分布式