13518219792

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

消息中间件:RabbitMQ原理、集群、基本运维操作、常见故障处理

本次学习主要针对运维人员,和对rabbitmq不熟悉的开发人员。通过本次学习你将掌握rabbitmq 的基本原理、集群、基本运维操作、常见故障处理。

坚守“ 做人真诚 · 做事靠谱 · 口碑至上 · 高效敬业 ”的价值观,专业网站建设服务10余年为成都成都VR全景小微创业公司专业提供企业网站建设营销网站建设商城网站建设手机网站建设小程序网站建设网站改版,从内容策划、视觉设计、底层架构、网页布局、功能开发迭代于一体的高端网站建设服务。

1、原理与概念

简介

AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。

AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

解决的问题

RabbitMQ就是当前主流的消息中间件之一。

Queue

Queue(队列)是RabbitMQ的内部对象,用于存储消息,用下图表示。

RabbitMQ中的消息都只能存储在Queue中,生产者(下图中的P)生产消息并最终投递到Queue中,消费者(下图中的C)可以从Queue中获取消息并消费。

多个消费者可以订阅同一个Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。

技术术语

消息队列的使用过程

在AMQP模型中,Exchange是接受生产者消息并将消息路由到消息队列的关键组件。ExchangeType和Binding决定了消息的路由规则。所以生产者想要发送消息,首先必须要声明一个Exchange和该Exchange对应的Binding。

在Rabbit MQ中,声明一个Exchange需要三个参数:ExchangeName,ExchangeType和Durable。ExchangeName是该Exchange的名字,该属性在创建Binding和生产者通过publish推送消息时需要指定。ExchangeType,指Exchange的类型,在RabbitMQ中,有三种类型的Exchange:direct ,fanout和topic,不同的Exchange会表现出不同路由行为。Durable是该Exchange的持久化属性,这个会在消息持久化章节讨论。

声明一个Binding需要提供一个QueueName,ExchangeName和BindingKey。

下面是消息发送的过程:

exchange 与 Queue 的路由机制

exchange 将消息发送到哪一个queue是由exchange type 和bing 规则决定的,目前常用的有3种exchange,Direct exchange, Fanout exchange, Topic exchange 。Direct exchange 直接转发路由,其实现原理是通过消息中的routkey,与queue 中的routkey 进行比对,若二者匹配,则将消息发送到这个消息队列。通常使用这个。

以上图的配置为例,我们以routingKey=”error”发送消息到Exchange,则消息会路由到Queue1(amqp.gen-S9b…,这是由RabbitMQ自动生成的Queue名称)和Queue2(amqp.gen-Agl…);如果我们以routingKey=”info”或routingKey=”warning”来发送消息,则消息只会路由到Queue2。如果我们以其他routingKey发送消息,则消息不会路由到这两个Queue中。

Fanout exchange 复制分发路由,该路由不需要routkey,当exchange收到消息后,将消息复制多份转发给与自己绑定的消息队列。

上图中,生产者(P)发送到Exchange(X)的所有消息都会路由到图中的两个Queue,并最终被两个消费者(C1与C2)消费。topic exchange 通配路由,是direct exchange的通配符模式,消息中的routkey可以写成通配的模式,exchange支持“#”和“*” 的通配。收到消息后,将消息转发给所有符合匹配表达式的queue。

以上图中的配置为例,routingKey=”quick.orange.rabbit”的消息会同时路由到Q1与Q2,routingKey=”lazy.orange.fox”的消息会路由到Q1,routingKey=”lazy.brown.fox”的消息会路由到Q2,routingKey=”lazy.pink.rabbit”的消息会路由到Q2(只会投递给Q2一次,虽然这个routingKey与Q2的两个bindingKey都匹配);routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的消息将会被丢弃,因为它们没有匹配任何bindingKey。

需要注意的一点只有queue具有 保持消息的功能,exchange不能保存消息。

headers headers类型的Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。 在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。 该类型的Exchange没有用到过(不过也应该很有用武之地),所以不做介绍。

durability 持久化与非持久化队列

如何识别? 如上图,在Features字段里有一个D,就是持久化队列,英文durable(持久的)。

持久化队列和非持久化队列的区别是什么? 持久化队列会被保存在磁盘中,固定并持久的存储,当Rabbit服务重启后,该队列会保持原来的状态在RabbitMQ中被管理,而非持久化队列不会被保存在磁盘中,Rabbit服务重启后队列就会消失。

如何选择? 如果需要队列的完整性,数据在队列中的保存是必须不允许丢失的,那么可以使用持久化。而当需要获取的信息是实时的,或者是随机的信息,不需要信息的精确性或完整性,但是追求获取性能,可以选择非持久化队列。

2、分布式集群架构和高可用性

设计集群的目的:

集群配置方式:

RabbitMQ可以通过三种方法来部署分布式集群系统,分别是:cluster,federation,shovel

不支持跨网段,用于同一个网段内的局域网

可以随意的动态增加或者减少

节点之间需要运行相同版本的RabbitMQ和Erlang。

RabbitMQ cluster 集群同步原理

上面图中采用三个节点组成了一个RabbitMQ的集群,Exchange A的元数据信息在所有节点上是一致的,而Queue(存放消息的队列)的完整数据则只会存在于它所创建的那个节点上。,其他节点只知道这个queue的metadata信息和一个指向queue的owner node的指针。RabbitMQ集群元数据的同步。

RabbitMQ集群会始终同步四种类型的内部元数据(类似索引):

为何RabbitMQ集群仅采用元数据同步的方式

一,存储空间,如果每个集群节点都拥有所有Queue的完全数据拷贝,那么每个节点的存储空间会非常大,集群的消息积压能力会非常弱(无法通过集群节点的扩容提高消息积压能力); 二,性能,消息的发布者需要将消息复制到每一个集群节点,对于持久化消息,网络和磁盘同步复制的开销都会明显增加。

RabbitMQ cluster 集群的两种模式

  1. 普通模式:默认的集群模式。
  2. 镜像模式:把需要的队列做成镜像队列,存在于多个节点,属于RabbitMQ的HA方案。

普通模式:当消息进入A节点的Queue中后,consumer从B节点拉取时,RabbitMQ会临时在A、B间进行消息传输,把A中的消息实体取出并经过B发送给consumer,所以consumer应平均连接每一个节点,从中取消息。该模式存在一个问题就是当A节点故障后,B节点无法取到A节点中还未消费的消息实体。如果做了队列持久化或消息持久化,那么得等A节点恢复,然后才可被消费,并且在A节点恢复之前其它节点不能再创建A节点已经创建过的持久队列;如果没有持久化的话,消息就会失丢。这种模式更适合非持久化队列,只有该队列是非持久的,客户端才能重新连接到集群里的其他节点,并重新创建队列。假如该队列是持久化的,那么唯一办法是将故障节点恢复起来。为什么RabbitMQ不将队列复制到集群里每个节点呢?这与它的集群的设计本意相冲突,集群的设计目的就是增加更多节点时,能线性的增加性能(CPU、内存)和容量(内存、磁盘)。当然RabbitMQ新版本集群也支持队列复制(有个选项可以配置)。比如在有五个节点的集群里,可以指定某个队列的内容在2个节点上进行存储,从而在性能与高可用性之间取得一个平衡(应该就是指镜像模式)。

镜像模式:其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在consumer取数据时临时拉取。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用。

节点类型

RAM node:内存节点将所有的队列、交换机、绑定、用户、权限和vhost的元数据定义存储在内存中,好处是可以使得像交换机和队列声明等操作更加的快速。

Disk node:将元数据存储在磁盘中,单节点系统只允许磁盘类型的节点,防止重启RabbitMQ的时候,丢失系统的配置信息。

如果是内存结点这里就显示为RAM注意:

Erlang Cookie

Erlang Cookie是保证不同节点可以相互通信的密钥,要保证集群中的不同节点相互通信必须共享相同的Erlang Cookie。具体的目录存放在/var/lib/rabbitmq/.erlang.cookie。

3、基本运维操作

rabbitmq集群必要条件

绑定实体ip,即ifconfig所能查询到的绑定到网卡上的ip,以下是绑定方法

 
 
 
 
  1. #编辑配置路径 /etc/rabbitmq/rabbitmq-env.conf 
  2. NODE_IP_ADDRESS=172.16.136.133 
  3. 复制代码 

配置域名映射到实体ip

 
 
 
 
  1. #配置文件1所在路径 /etc/rabbitmq/rabbitmq.config (如果是集群,每台机器都需要修改这个绑定本机实体ip) 
  2. #其中rabbit@master是创建集群时所配置的参数,@后面的参数为主机名,示例中为master 
  3.  {rabbit, [ 
  4.  {cluster_nodes, {['rabbit@master'], disc}}, 
  5.  {cluster_partition_handling, ignore}, 
  6.  {default_user, <<"guest">>}, 
  7.  {default_pass, <<"guest">>}, 
  8.  {tcp_listen_options, [binary, 
  9.  {packet, raw}, 
  10.  {reuseaddr, true}, 
  11.  {backlog, 128}, 
  12.  {nodelay, true}, 
  13.  {exit_on_close, false}, 
  14.  {keepalive, true}]} 
  15.  ]}, 
  16.  {kernel, [ 
  17.  {inet_dist_listen_max, 44001}, 
  18.  {inet_dist_listen_min, 44001} 
  19.  ]} 
  20. ]. 
  21. 复制代码 
  22. #配置文件2 所在路径 /etc/hosts (如果是集群,每台机器都需要修改这个绑定本机实体ip,而且hosts文件的映射不得重复,如果重复linux系统为以最下面一条记录为准) 
  23. 172.16.136.133 master 
  24. 172.16.136.134 venus 
  25. 172.16.136.135 venus2 

启动停止

停止

 
 
 
 
  1. #机器A 
  2. service rabbitmq-server stop 
  3. epmd -kill 
  4. #机器B 
  5. service rabbitmq-server stop 
  6. epmd -kill 
  7. #机器C 
  8. service rabbitmq-server stop 
  9. epmd -kill 

启动

方式1

 
 
 
 
  1. #机器A 
  2. service rabbitmq-server start 
  3. #机器B 
  4. service rabbitmq-server start 
  5. #机器C 
  6. service rabbitmq-server start 

方式2

 
 
 
 
  1. rabbitmq-server -detached 

集群重启顺序

集群重启的顺序是固定的,并且是相反的。 如下所述:

启动顺序:磁盘节点 => 内存节点 关闭顺序:内存节点 => 磁盘节点 最后关闭必须是磁盘节点,不然可能回造成集群启动失败、数据丢失等异常情况。

重建集群

注1:此处的mq集群重建是比较快速和有效的方法,面向的是初次安装或者可以接受mq中所存有的数据丢失的情况下,必须先有mq的.json后缀的配置文件或者有把握写入集群中exchange、queue等配置。

按顺序停止所有机器中的rabbitmq

 
 
 
 
  1. #机器A 
  2. service rabbitmq-server stop 
  3. epmd -kill 
  4. #机器B 
  5. service rabbitmq-server stop 
  6. epmd -kill 
  7. #机器C 
  8. service rabbitmq-server stop 
  9. epmd -kill 

移除rabbitmq配置记录与存储文件

 
 
 
 
  1. #位于 /var/lib/rabbitmq/mensia 
  2. mv /var/lib/rabbitmq/mensia /var/lib/rabbitmq/mensia.bak 

按顺序启动所有机器中的rabbitmq

 
 
 
 
  1. #机器C 
  2. service rabbitmq-server start 
  3. #机器B 
  4. service rabbitmq-server start 
  5. #机器A 
  6. service rabbitmq-server start 

停止被加入集群节点app

 
 
 
 
  1. 比如A、B、C三台机器,将B和C加入到A中去,需要执行以下命令 
  2. #机器B 
  3. rabbitmqctl stop_app 
  4. #机器C 
  5. rabbitmqctl stop_app 

建立集群

 
 
 
 
  1. 注意此处master为唯一没有执行rabbitmqctl stop_app的机器 
  2. #机器B 
  3. rabbitmqctl join_cluster rabbit@master 
  4. #机器C 
  5. rabbitmqctl join_cluster rabbit@master 

启动集群

 
 
 
 
  1. #机器B 
  2. rabbitmqctl start_app 
  3. #机器C 
  4. rabbitmqctl start_app 
  5. 复制代码 

检查集群状态

在任意一台机器上执行rabbitmqctl cluster_status命令即可检查,输出包含集群中的节点与运行中的节点,兼以主机名标志

添加集群配置

创建用户

例子中创建了两个用户 添加用户add_user,设置角色set_user_tags,添加rabbitmq虚拟主机add_vhost,设置访问权限set_permissions,以下是详细用法

 
 
 
 
  1. 例子中创建了两个用户 添加用户add_user,设置角色set_user_tags,添加rabbitmq虚拟主机add_vhost,设置访问权限set_permissions,以下是详细用法 
  2.  # 创建第一个用户 
  3.  /usr/sbin/rabbitmqctl add_user 用户名 密码 
  4.  /usr/sbin/rabbitmqctl set_user_tags 用户名 administrator 
  5.  /usr/sbin/rabbitmqctl set_permissions -p / 用户名 ".*" ".*" ".*" 
  6.  # 创建第二个用户 
  7.  /usr/sbin/rabbitmqctl add_user 用户名2 密码 
  8.  /usr/sbin/rabbitmqctl set_user_tags 用户名2 management  
  9.  /usr/sbin/rabbitmqctl add_vhost sip_ext  
  10.  /usr/sbin/rabbitmqctl set_permissions -p sip_ext 用户名2 '.*' '.*' '.*'  
  11. 复制代码 
  12. 备注:RabbitMQ 虚拟主机,RabbitMQ 通过虚拟主机(vhost)来分发消息。拥有自己独立的权限控制,不同的vhost之间是隔离的,单独的。 
  13. 权限控制的基本单位:vhost。 
  14. 用户只能访问与之绑定的vhost。 
  15. vhost是AMQP中唯一无法通过协议来创建的基元。只能通过rabbitmqctl工具来创建。  

打开15672网页管理端,访问mq

/usr/sbin/rabbitmq-plugins enable rabbitmq_management 备注:如果发现命令执行完毕没有打开此服务,15672端口没有监听,则是由于没有重启mq导致的

在底部导入.json后缀的配置文件即可

http://localhost:4000/first-blog/rabbitmq.jpg

如果覆盖了用户需要使用以下命令修改mq用户密码 /usr/sbin/rabbitmqctl change_password 用户名 密码

修改节点类型

 
 
 
 
  1. rabbitmqctl stop_app 
  2. rabbitmqctl change_cluster_node_type dist 
  3. rabbitmqctl change_cluster_node_type ram 
  4. rabbitmqctl start_app 

常用命令

4、常见故障

集群状态异常

  1. rabbitmqctl cluster_status检查集群健康状态,不正常节点重新加入集群
  2. 分析是否节点挂掉,手动启动节点。
  3. 保证网络连通正常
  1. 保证网络连通正常
  2. 保证消费者正常消费,消费速度大于生产速度
  3. 保证服务器TCP连接限制合理

脑裂

按正确顺序重启集群

保证网络连通正常

保证磁盘空间、cpu、内存足够


文章名称:消息中间件:RabbitMQ原理、集群、基本运维操作、常见故障处理
浏览地址:http://cdbrznjsb.com/article/dpeodoi.html

其他资讯

让你的专属顾问为你服务