RabbitMQ(概念+AMQP协议)

RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。

生产者和消费者

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

    生产者创建消息,然后发布到 RabbitMQ 中。消息一般可以包含2个部分:消息体和标签(Label)。消息体也可以称之为 payload,在实际应用中,消息体一般是一个带有业务逻辑结构的数据。消息的标签用来表述这条消息 比如一个交换器的名称和一个路由键。

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

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

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

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

队列 

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

    RabbitMQ 中消息都只能存储在队列中,这一点跟 Katka 这种消息中间件相反。Katka 将消息存储在 topic(主题)这个逻辑层面,而相对应的队列逻辑只是 topic 实际存储文件中的位移标识。 RabbitMQ 的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。

    多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊 (Round-Robin ,即轮询) 给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。

交换器、路由键、绑定

Exchange: 交换器。生产者将消息发送到 Exchange ,由交换器将消息路由到一个或者多个队列中。如果路由不到,或许会返回给生产者,或许直接丢弃。RabbitMQ 中的交换器有四种类型,不同的类型有着不 同的路由策略。

RoutingKey: 路由键。生产者将消息发给交换器的时候, 一般会指定一个 RoutingKey ,用来指定这个消息的路由规则,而这个 RoutingKey 需要与交换器类型和绑定键 (BindingKey) 联合使用才能最终生效。

    在交换器类型和绑定键 (BindingKey) 固定的情况下,生产者可以在发送消息给交换器时, 通过指定 RoutingKey 来决定消息流向哪里。

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

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

        Connection connection=factory.newConnection();//创建连接
        Channel channel=connection.createChannel();//创建通道
        // 创建一个 type="direct" 、持久化的、非自动删除的交换器
        channel.exchangeDeclare(EXCHANGE_NAME,"direct",true,false,null);
        //创建一个持久化、非排他的、非自动删除的队列
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);
        //将交换器与队列通过路由键绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,ROUTING_KEY);
        //发送一条持久化的消息
        String message="发送的消息@@@@@@@!!!!";
        channel.basicPublish(EXCHANGE_NAME , ROUTING_KEY ,
                MessageProperties.PERSISTENT_TEXT_PLAIN ,
                message.getBytes()) ;

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

    但是在 topic 交换器类型下, RoutingKey 和 BindingKey 之间需要做模糊匹配,两者并不是相同的。

  • 在使用绑定的时候,其中需要的路由键是 BindingKey 。涉及的客户端方法如: channel.exchangeBind、channel.queueBind ,对应的 AMQP 命令为 Exchange.Bind、Queue.Bind。
  • 在发送消息的时候,其中需要的路由键是 RoutingKey 。涉及的客户端方法如 channel.basicPublish ,对应的 AMQP 命令为 Basic.Publish。

交换器类型 

RabbitMQ 常用的交换器类型有 fanout、direct、topic、headers 这四种。 AMQP 协议里还提 到另外两种类型: System 和自定义。

fanout:它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。

direct:direct 类型的交换器它会把消息路由到那些 BindingKey 和 RoutingKey 完全匹配的队列中。

topic:topic 类型的交换器在匹配规则上进行了扩展,它与 direct 类型的交换器相似,也是将消息路由到 BindingKey 和RoutingKey 相匹配的队列中,但这里的匹配规则有些不同:

  • RoutingKey 为一个点号"."分隔的字符串(被点号"."分隔开的每一段独立的字符串称为一个单词 ),如 "com.rabbitmq.client"、"com.hidden.client";
  • BindingKey 和 RoutingKey 一样也是点号"."分隔的字符串;
  • BindingKey 中可以存在两种特殊字符串"*"和"#",用于做模糊匹配,其中"*"用于匹配一个单词,"#"用于匹配多规格单词(可以是零个)。

headers:headers 类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中 headers 属性进行匹配。在绑定队列和交换器时制定一组键值对,当发送消息到交换器时, RabbitMQ 会获取到该消息的 headers (也是一个键值对的形式) ,对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers 类型的交换器性能会很差,而且也不实用,基本上不会看到它的存在。

RabbitMQ 运转流程

生产者发送消息:

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

消费者接收消息的过程: 

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

    Connection和Channel:无论是生产者还是消费者,都需要和 RabbitMQ Broker 建立连接,这个连接就是一条 TCP 连接,也就是 Connection。一旦 TCP 连接建立起来,客户端紧接着可以创建一个 AMQP 信道 (Channel) ,每个信道都会被指派一个唯一的ID 。信道是建立在 Connection 之上的虚拟连接, RabbitMQ 处理的每条 AMQP 指令都是通过信道完成的。 RabbitMQ 采用类似 NIO (Non-blocking I/ O) 的做法,选择 TCP 连接复用,不仅可以减少性能开销,同时也便于管理。

    每个线程把持一个信道,所以信道复用了 Connection 的 TCP 连接。同时 RabbitMQ 可以确保每个线程的私密性,就像拥有独立的连接一样。当每个信道的流量不是很大时,复用单一的 Connection 可以在产生性能瓶颈的情况下有效地节省 TCP 连接资源。但是当信道本身的流量很大时,这时候多个信道复用一个 Connection 就会产生性能瓶颈,进而使整体的流量被限制了。 此时就需要开辟多个 Connection ,将这些信道均摊到这些 Connection 中。

AMQP 协议介绍

    AMQP 的模型架构和 RabbitMQ 的模型架构是一样的,生产者将消息发送给交换器,交换器和队列绑定 。当生产者发送消息时所携带的 RoutingKey 与绑定时的 BindingKey 匹配时,消息即被存入相应的队列之中。 消费者可以订阅相应的队列来获取消息。

AMQP 协议本身包括三层:

  • Module Layer: 位于协议最高层,主要定义了一些供客户端调用的命令,客户端可以利 用这些命令实现自己的业务逻辑。例如,客户端可以使用 Queue.Declare 命令声明一个队列或者使用 Basic.Consume 订阅消费一个队列中的消息。
  • Session Layer: 位于中间层,主要负责将客户端的命令发送给服务器,再将服务端的应答返回给客户端,主要为客户端与服务器之间的通信提供可靠性同步机制和错误处理。
  • Transport Layer: 位于最底层,主要传输二进制数据流,提供帧的处理、信道复用、错误检测和数据表示等。

AMQP 命令概览 

参考《RabbitMQ实战指南》

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值