消息中间件之RabbitMQ

一、RabbitMQ简介

1、简介

RabbitMQ是一个遵循AMQP协议的消息中间件,它从生产者接收消息并递送给消费者,在这个过程中,根据规则进行路由,缓存与持久化。

  1. 基于erlang语言开发具有高可用高并发的优点,适合集群服务器
  2. 可靠性:包括消息持久化,消费者和生产者的消息确认
  3. 灵活路由:遵循AMQP协议,支持多种Exchange类型实现不同路由策略
  4. 分布式:集群的支持,包括本地网络与远程网络
  5. 高可用性:支持主从备份与镜像队列
  6. 多语言支持:支持多语言的客户端
  7. WEB界面管理:可以管理用户权限,exhange,queue,binding,与实时监控
  8. 访问控制:基于vhosts实现访问控制
  9. 调试追踪:支持tracing,方便调试
  10. 开源免费

【较其他MQ】
- Apache ActiveMQ 比较出名,但是有丢失消息的风险。
- ZeroMQ延迟很低、支持灵活拓扑,但是不支持消息持久化和崩溃恢复

2、核心概念

  1. producer:消息生产者,就是投递消息的程序

  2. consumer:消息消费者,就是接受消息的程序

  3. Message是RabbitMQ中的消息体
  4. Broker:简单来说就是消息队列服务器实体
  5. channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务
  6. Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列
  7. Routing Key:路由关键字,exchange根据这个关键字进行消息投递
  8. Queue:消息队列载体,每个消息都会被投入到一个或多个队列
  9. Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来

  10. vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离

二、RabbitMQ的架构

1、流程解析

a. 通信方式

RabbitMQ是基于AMQP协议来实现的消息中间件。AMQP,类似于HTTP协议,也是一个应用层的协议,网络层使用TCP来通信。因此,RabbitMQ也是典型的C-S模型,准确地说是C-S-C模型,因为伴随着RabbitMQ的使用,总是会有Producer与Consumer两个Client和一个Broker Server

image

Client要与Server进行通信,就必须先建立连接,RabbitMQ中有Connection与Channel两个概念,前者就是一个TCP连接,后者是在这个连接上的虚拟概念,负责逻辑上的数据传递,因此,为了节省资源,一般在一个客户端中建立一个Connection,每次使用时再分配一个Channel即可

b.消息体

Message是RabbitMQ中的消息体概念。类似HTTP传输中,有header和body两部分数据,Message中也有Attributes和Payload两部分数据,前者是一些元信息,后者是传递的消息数据实体

image

Spring-AMQP 允许我们直接使用自定义的类,然后会利用指定好的 MessageConverter 将自定义的类转换为 Message 进行发送,在接收时也会利用 MessageConverter 将接收到的 Message 对象转成需要的对象。Spring-AMQP 提供了多种 MessageConverter,比如 SimpleMessageConverter,SerializerMessageConverter,Jackson2JsonMessageConverter,MarshallingMessageConverter等等,如果发送的消息对象不是 Message 实例,并且没有指定 MessageConverter 的话,默认用 SimpleMessageConverter。

比较常用的 Converter 就是 Jackson2JsonMessageConverter(以下简称 JsonMessageConverter),在发送消息时,它会先将自定义的消息类序列化成json格式,再转成byte构造 Message,在接收消息时,会将接收到的 Message 再反序列化成自定义的类。大致流程如下图:
image

不过使用 JsonMessageConverter 时有一个小问题,==在不对它进行任何改造的前提下==,发送消息的类和接受消息的类必须是一样的,不仅是要里面的字段一样,类名一样,连类的包路径都要一样。

c.消息发送

Exchange、Queue与Routing Key三个概念是理解RabbitMQ消息投递的关键。RabbitMQ中一个核心的原则是,消息不能直接投递到Queue中。Producer只能将自己的消息投递到Exchange中,由Exchange按照routing_key投递到对应的Queue中,消费端进行监听消费消息。

image

那么,具体实现时,如何完成这三者关系的绑定?总结起来是两点:
- 在Consumer Worker中,声明自己对哪个Exchange感兴趣,并将自己的Queue绑定到自己感兴趣的一组routing_key上,建立相应的映射关系;
- 在Producer中,将消息投递一个Exchange中,并指明它的routing_key。由此可见,Queue这个概念只是对Consumer可见,Producer并不关心消息被投递到哪个Queue中。

看过RabbitMQ的”Hello World”教程的童鞋可能会发现在那里面的图中并没有看到Exchange和routing_key的踪迹,但这并不意味着RabbitMQ可以支持直接将消息投递到Queue中,而是在内部使用了默认的Exchange和routing_key了。默认情况下,RabbitMQ使用名称为“amq.direct”的Direct Exchange,routing_key默认名字与Queue保持一致。

搞清楚上述概念,就不难理解Exchange的四种类型了。Direct、Fanout、Topic、Headers,区别在于如何将消息从Exchange投递到Queue中。Direct使用具体的routing_key来投递;Fanout则忽略routing_key,直接广播给所有的Queue;Topic是使用模糊匹配来对一组routing_key进行投递;Headers也是忽略routing_key,使用消息中的Headers信息来投递。

2、交换机

RabbitMQ提供了四种Exchange:fanout,direct,topic,header

a. Direct Exchange

image

  • [x] 一般情况可以使用rabbitMQ自带的Exchange:””(该Exchange的名字为空字符串,下文称其为default Exchange)。

  • [x] 这种模式下不需要将Exchange进行任何绑定(binding)操作

  • [x] 消息传递时需要一个“RouteKey”,可以简单的理解为要发送到的队列名字。

  • [x] 如果vhost中不存在RouteKey中指定的队列名,则该消息会被抛弃。

b.Fanout Exchange

image

  • [x] 可以理解为路由表的模式

  • [x] 这种模式不需要RouteKey

  • [x] 这种模式需要提前将Exchange与Queue进行绑定,一个Exchange可以绑定多个Queue,一个Queue可以同多个Exchange进行绑定。

  • [x] 如果接受到消息的Exchange没有与任何Queue绑定,则消息会被抛弃

c.Topic Exchange

image

  • [x] 这种模式较为复杂,简单来说,就是每个队列都有其关心的主题,所有的消息都带有一个“标题”(RouteKey),Exchange会将消息转发到所有关注主题能与RouteKey模糊匹配的队列。

  • [x] 这种模式需要RouteKey,也许要提前绑定Exchange与Queue。

  • [x] 在进行绑定时,要提供一个该队列关心的主题,如“#.log.#”表示该队列关心所有涉及log的消息(一个RouteKey为”MQ.log.error”的消息会被转发到该队列)。

  • [x] “#”表示0个或若干个关键字,“”表示一个关键字。如“log.”能与“log.warn”匹配,无法与“log.warn.timeout”匹配;但是“log.#”能与上述两者匹配。

性能排序:fanout > direct >> topic。比例大约为11:10:6

三、走进RabbitMQ

1、RabbitMQ下的生产消费者模式与发布订阅模式

生产消费者模式与订阅发布模式是使用消息中间件时常用的两种模式,用于功能解耦和分布式系统间的消息通信,以下面两种场景为例:

a. 数据上报

类似于电商平台统计用户行为的数据处理,浏览商品到推荐商品。app端负责发送用户的行为信息,后端把消息发送到队列里,完事了。具体的消息处理环节由其他专业团队负责处理,以及推送相关信息。

image

这样做的好处有:
- 能分离,发送统计消息而不关心数据处理功能,只负责接入数据;
- 数据缓冲,数据上报的速率是不可控的,取决于用户使用频率,采用该模式可以一定程度地缓冲数据;
- 易于扩展,在数据量大时,通过增加数据处理Worker来扩展,提高处理速率。这便是典型的生产消费者模式,数据上报为生产者,数据处理为消费者

b.事件分发

用户”预约挂号”、”付款”、行为都是非常重要的事件,通常后端服务在完成相应的功能处理外,还需要在这些事件点上做很多其他处理动作,比如发送短信通知、记录用户积分等等。我们可以将这些额外的处理动作放到一个事件分发系统,在各个功能模块中将对应的事件发布出来,由对其感兴趣的处理者进行处理。这里涉及两个角色:A对B感兴趣,A是处理者,B是事件,由事件处理器完成二者的绑定,并向消息中心订阅事件。服务模块是后端的业务逻辑服务,在不同的事件点发布事件,事件经过消息中心分发给事件处理器对应的处理者。整个流程如下图所示。这边是典型的订阅发布模式。

image

2、生产消费模式

image

对于上报的数据,如果是special的行为,需要优先处理。从上图可以看到,数据上报端负责将数据投递到RabbitMQ对应的Exchange,并指明routing_key是common还是special。数据处理端,可以根据情况启多个Woker来消费数据,但至少需要两个,一个用来处理common数据,一个用来处理special的数据。

3、订阅发布模式

架构如下图所示,使用event name/id来作为RabbitMQ的routing key的名字。Event Processor 01对event 01 和event 02感兴趣,则在启动Consumer Worker时,将自己的Queue绑定到这两个routing key上即可,其他Event Processor也是如此,这样便完成了事件的订阅。当有事件发布时,消息便会按照event name/id被投递到对应的Queue中

image

由此可见,在不同的应用中,变化的只是routing_key与Consumer Queue的绑定关系,在充分理解RabbitMQ的核心概念后处理这些应该也是得心应手了。

四、集群的魅力

1、为毛要使用集群

RabbitMQ基于Erlang编写,天然支持clustering。集群是保证可靠性的一种方式,同时可以通过水平扩展以达到增加消息吞吐能力的目的

2.单机搭建集群

可以参考之前的一片mq搭建说明
#节点1
./rabbitmq-server -detached
或者
RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit@zhanghuilongdeMacBook-Pro ./rabbitmq-server -detached

#节点2 
RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}] -rabbitmq_stomp tcp_listeners [61614] -rabbitmq_mqtt  tcp_listeners [1884]" RABBITMQ_NODENAME=rabbit1@zhanghuilongdeMacBook-Pro ./rabbitmq-server -detached

#节点3
RABBITMQ_NODE_PORT=5674 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15674}] -rabbitmq_stomp tcp_listeners [61615] -rabbitmq_mqtt tcp_listeners [1885]" RABBITMQ_NODENAME=rabbit2@zhanghuilongdeMacBook-Pro ./rabbitmq-server -detached

集群操作

1.停止第二个节点的应用程序
$ ./rabbitmqctl -n rabbit1@zhanghuilongdeMacBook-Pro stop_app

2.重新设置第二个节点的元数据和状态为清空状态。
$ ./rabbitmqctl -n rabbit1@zhanghuilongdeMacBook-Pro reset

3.加入第一节点
./rabbitmqctl -n rabbit1@zhanghuilongdeMacBook-Pro join_cluster rabbit@zhanghuilongdeMacBook-Pro
(  join_cluster <clusternode> [--ram]设置节点类型)

4.重启第二个节点
$  ./rabbitmqctl -n rabbit1@zhanghuilongdeMacBook-Pro start_app

-detached选项以后台方式启动可能错过很多日志信息。

rabbitmq-server仅能够启动Broker。管理Broker需使用rabbitmqctl命令来执行。

3.rabbitmq 日志

[email protected]: 用于记录Erlang相关信息,例如Erlang的崩溃报告

[email protected]:真正的rabbitmq日志,可以看到用户、交换器、队列等的操作事件,例如某些原因导致AMQP客户端编码请求失败,声明队列冲突,都可以在此看到相关事件记录。

4. 集群中的注意事项:

image
a.Exchange 的metadata信息在所有节点上是一致的。可以比做:路由模式列表和匹配消息应发往的队列进程pid 列表。发布的消息匹配交换器
的绑定队则后,实际上是由信道完成了匹配工作,并建立了到指定队列的链接,然后投递消息。

b.queue的完整信息则只在它创建的那个节点上,其元数据会在所有节点上。

c.磁盘节点/内存节点
rabbit要求至少存在一个磁盘节点。所有变更要通知磁盘节点。
磁盘节点挂了,集群可以正常运行,但是无法更改任何东西了。

。。。

5.负载均衡

负载均衡器:软硬件两种
HAProxy….待续

其他更多MQ视频资料:
http://120.55.52.75:8080/dataBank/detail?resourceId=mq-qwe-001

猜你喜欢

转载自blog.csdn.net/java_huilong/article/details/79069479