RabbitMQ各功能入门

喜你入骨 提交于 2019-11-30 22:45:17

一、RabbitMQ相关角色

RabbitMQ整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息,可以把消息传递的过程想象成:你将一个包裹收到邮局,邮局会暂存并最终将包裹由快递员送到收件人的手上。RabbitMQ就好比由邮局、邮箱和邮递员组成的一个系统,从计算机术语来说,RabbitMQ模型更像是一种交换机模型。如下图(RabbitMQ的模型框架):


二、生产者和消费者

Ⅰ:Producer:生产者,就是投递消息的一方。

生产者创建消息,然后发布到RabbitMQ中,消息一般可以包含2个部分:消息体和标签(label)。也可成为payload,在应用中,消息体一般是一个带有业务逻辑结构的数据,如JSON串。生产者把消息交给RabbitMQ,RabbitMQ之后会根据标签把消息发送给感兴趣的消费者(Consumer)。

Ⅱ:Consumer:消费者,就是接收消息的一方。

消费者连接到RabbitMQ服务器,并订阅到队列上。当消费者消费一条消息时,只是消费消息的消息体(payload),在消息路由的过程中,消息的标签会丢弃,存入到队列中的消息只有消息体,消费者也只会消费到消息体,也就不知道消息的生产者是谁,也不需要知道。

Ⅲ:Broker:消息中间件的服务节点。

对于RabbitMQ来说,一个RabbitMQ Broker可以简单的看作一个RabbitMQ服务节点,或者RabbitMQ服务实例。大多数情况下可以将RabbitMQ Broker看作一台RabbitMQ服务器。

Ⅳ:整个流程,如图:

生产者将消息存入RabbitMQ Broker中,消费者从Broker中消费数据的整个过程:

首先生产者将业务方数据进行可能的包装,然后封装成消息,发送到Broker中,消费者订阅并接收消息,经过可能的解包处理得到原始数据,在进行业务逻辑处理。这个业务逻辑处理并不一定需要和接收消息的逻辑使用同一个线程,消费者可以使用一个线程去接收消息,存到内存中,比如使用java的BlockingQueue。业务处理逻辑使用另一个线程从内存中读取数据,这样可以将应用进一步解耦,提供整个应用的处理效率。


三、队列

Ⅰ:Queue:队列,是RabbitMQ的内部对象,用于存储消息。

RabbitMQ中消息都只能存储在队列中,这一点和Kafka这种消息中间件相反。Kafka将消息存储在topic这个逻辑层。RabbitMQ的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(Round-Robin,轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。


四、交换器、路由键、绑定

Ⅰ:Exchange:交换器。

生产者将消息发送到Exchange(交换器,通常用大写的“X”表示),由交换器将消息路由到一个或者多个队列中,如果路由不到,或许会返回给生产者,或许直接丢弃,这里可以将RabbitMQ中的交换器看作一个简单的实体。


RoutingKey:路由键。生产者将消息发给交换器的时候,一般会指定一个RoutingKey,用来指定这个消息的路由规则,而这个RoutingKey需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。在交换器类型和绑定键固定的情况下,生产者可以在发送消息给交换器时,通过指定的RoutingKey来决定消息流向哪里。

Binding:绑定。RabbitMQ中通过绑定将交换器与队列关联起来,在绑定的时候一般会指定一个绑定键(BindingKey),这样RabbitMQ知道如果正确的将消息路由到队列了。


生产者将消息发送给交换器时,需要一个RoutingKey,当BindingKey和RoutingKey相匹配时,消息会被路由到对应的队列中。在绑定多个队列到同一个交换器的时候,这些绑定允许使用相同的BindingKey。BindingKey并不是在所有的情况下都生效,它依赖于交换器类型,比如fanout类型的交换器就会无视BindingKey,而是将消息路由到所有绑定到该交换器的队列中。

示例代码:

public class RabbitProducer {

    private static final String DEMO_NAME = "exchange_demo";
    private static final String ROUTING_KEY = "routingkey_demo";
    private static final String QUEUE_NAME = "queue_name";
    private static final String IP_ADDRESS = "127.0.0.1";
    private static final int PORT = 5672;
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root123";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(IP_ADDRESS);
        factory.setPort(PORT);
        factory.setUsername(USERNAME);
        factory.setPassword(PASSWORD);
        Connection connection = factory.newConnection(); //创建连接
        Channel channel = connection.createChannel(); //创建通道
        //创建一个type="direct"、持久化、非自动删除的交换器
        channel.exchangeDeclare(DEMO_NAME, "direct", true, false, null);
        //创建一个持久化、非排他的、非自动删除的队列
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //将交换器与队列通过路由键绑定
        channel.queueBind(QUEUE_NAME, DEMO_NAME, ROUTING_KEY);
        String message = "Hello RabbitMQ~"; //发送一条持久化的消息
        channel.basicPublish(DEMO_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
        channel.close();
        connection.close();//关闭资源
    }
}

以上代码声明了一个direct类型的交换器,然后将交换器和队列绑定起来。注意这里使用的字样是“ROUTING_KEY”,在本该使用BindingKey的channel.queueBind方法中却和channel.basicPublish方法同样使用了RoutingKey,这样做的潜台词是:这里的RoutingKey和BindingKey是同一个东西。在direct交换器类型下,RoutingKey和BindingKey需要完全匹配才能使用,所以上面代码中采用了此种写法会显得方便许多。

交换器相当于投递包裹的邮箱,RoutingKey相当于填写在包裹上的地址,BindingKey相当于包裹的目的地,当填写在包裹上的地址和实际想要投递的地址相匹配时,那么这个包裹就会被正确投递到目的地,最后这个目的地的“主人”——队列可以保留这个包裹。如果填写的地址出错,邮递员不能正确投递到目的地,包裹可能会回退给寄件人,也有可能被丢弃。

五、交换器类型

RabbitMQ常用的交换器类型有fanout、direct、topic、headers四种。

  1. fanout:它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。
  2. direct:它会把消息路由到那些BindingKey和RoutingKey完全匹配的队列中。如下图:warning、info、debug。
  3. topic:它与direct类型相似,但匹配规则不同。(a:RoutingKey为一个点“.”分隔的字符串,BindingKey也一样是“.”分隔;b:BindingKey中可以存在2种特殊字符串“*”和“#”,用于做模糊匹配,其中“*”匹配一个单词,“#”用于匹配多个单词)
  4. headers:不依赖于路由键的匹配规则来路由消息,而是根据发送的消息中的headers属性进行匹配。

六、RabbitMQ运转流程

1.生产者发送消息的过程

  • 1.生产者连接到RabbitMQ Broker,建立一个连接(connection),开启一个信道(channel);
  • 2.生产者声明一个交换器,并设置相关属性,如:交换机类型、是否持久化等;
  • 3.生产者声明一个队列并设置相关属性,比如是否排他,是否持久化、是否自动删除等;
  • 4.生产者通过路由键将交换器和队列绑定起来;
  • 5.生产者发送消息到RabbitMQ Broker,其中包含路由键、交换器等信息;
  • 6.相应的交换器根据接收到的路由键查找相匹配的队列;
  • 7.如果找到,则将从生产者发送过来的消息存入到响应的队列中;
  • 8.如果没有找到,则根据生产者配置属性选择是丢弃还是回退给生产者;
  • 9.关闭信道;
  • 10.关闭连接。

2.消费者接收消息的过程

  • 1.消费者连接到RabbitMQ Broker,建立一个连接(connection),开启一个信道(channel);
  • 2.消费者想RabbitMQ Broker请求消费相应的队列的消息,可能会设置相应的回调函数和一些准备工作;
  • 3.等待RabbitMQ Broker回应并投递相应队列的消息,消费者接收消息;
  • 4.消费者确认(ack)接收到的消息;
  • 5.RabbitMQ从队列中删除相应已经被确认的消息;
  • 6.关闭信道;
  • 7.关闭连接。




新手一枚,欢迎指正拍砖~ ~ ~

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!