RabbitMQ06-管理RabbitMQ集群
- RabbitMQ集群允许消费者和生产者在RabbitMQ一个节点崩惯的情况下继续运行,它也可以通过添加更多的节点来水平扩展消息通信的吞吐量。
- 当失去一个RabbitMQ 节点时,客户端能够重新连接到集群中的任何其他节点并继续生产或者消费。不过RabbitMQ集群不能保证消息的不会丢失,即便将消息、队列、交换器等都设置为可持久化,生产端和消费端也都正确地使用了确认方式。当集群中一个RabbitMQ节点崩溃时,该节点上的所有队列中的消息也会丢失。因为RabbitMQ集群中的一个节点只会备份其他节点的所有元数据信息,但是不会备份消息(可以通过特殊的配置比如镜像队列可以解决这个问题)。备份的元数据信息包括以下内容:
- 队列元数据:队列的名称及属性。
- 交换器元数据:交换器的名称及属性。
- 绑定关系元数据:交换器与队列或者交换器与交换器之间的绑定关系。
- vhost元数据:为vhost内的队列、交换器和绑定提供命名空间及安全属性。
- 基于存储空间和性能的考虑,在RabbitMQ集群中创建队列,集群只会在一个节点上有创建队列的进程并包含完整的队列信息(元数据、状态、内容),而不是在所有节点上。
- 这样只有队列的宿主节点知道队列的所有信息,所有其他非宿主节点只知道队列的元数据和指向该队列存在的那个节点的指针。
- 因此当集群节点崩溃时,该节点的队列进程和关联的绑定都会消失。附加在那些队列上的消费者也会丢失其所订阅的信息,并且任何路由到该队列的新消息也都会丢失。
- 不同于队列那样拥有自己的进程,交换器只是一个名称和绑定列表。当消息发布到交换器时,实际上是由信道将消息上的路由键同交换器的绑定列表进行比较,然后再路由消息。当创建一个新的交换器时,RabbitMQ所要做的就是将绑定列表添加到集群中的所有节点上。这样,每个节点上的每条信道都可以访问到新的交换器了。
- RabbitMQ集群对延迟非常敏感,应当只在本地局域网内使用。在广域网中不应该使用RabbirMQ集群,而应该使用Federation或者Shovel来代替。
1、对集群的基本操作
//查看集群的状态 rabbitmqctl [--node <node>] cluster_status //停止RabbitMQ服务,但不停止Erlang VM。 rabbitmqctl [--node <node>] stop_app //启动RabbitMQ服务,但不运行Erlang VM rabbitmqctl [--node <node>] start_app //使RabbitMQ节点脱离集群并返回到初始状态 rabbitmqctl [--node <node>] reset //使该节点成为指定节点所在集群的成员 rabbitmqctl [--node <node>] join_cluster [--disc|--ram] <existing_cluster_member> <existing_cluster_member>:要加入的集群中的成员(节点) --disc:将该节点设置为disk节点,将数据存储在磁盘中。推荐使用(默认使用)。 --ram:将该节点设置为RAM节点,将数据存储在内存中。不推荐。 //集群搭建完成后,修改节点类型 rabbitmqctl [--node <node>] change_cluster_node_type <disc | ram> //从集群中移除一个节点 rabbitmqctl [--node <node>] forget_cluster_node [--offline] <existing_cluster_member_node> --offline:可以将非运行状态下的节点剔除集群。 //强制节点启动,即使它不能联系或重新加入任何以前已知的节点 rabbitmqctl [--node <node>] force_boot
1.1、将节点加入集群
- 将新节点加入到集群中,要先使用rabbitmq-server -detached启动该节点。
//(1)停止RabbitMQ服务,但不停止Erlang VM。 rabbitmqctl [--node <node>] stop_app //(2)使RabbitMQ节点脱离集群并返回到初始状态 rabbitmqctl [--node <node>] reset //(3)使该节点成为指定节点所在集群的成员 rabbitmqctl [--node <node>] join_cluster [--disc|--ram] <existing_cluster_member> //(4)启动RabbitMQ服务,但不运行Erlang VM rabbitmqctl [--node <node>] start_app
1.2、将节点从集群中剔除
- 创建集群的过程可以看作向集群中添加节点的过程。那么如何将一个节点从集群中剔除呢?
- 有两种方式将RabbitMQ节点从集群中剔除:
- (1)先使用rabbitmqctl stop_app或者rabbitmqctl stop命令来关闭RabbitMQ服务,再使用rabbitmqctl forget_cluster_node命令将该RabbitMQ节点从集群中剔除。(这种方式适用该节点不再运行RabbitMQ)。
- (2)使用rabbitmqctl reset命令,使该RabbitMQ节点脱离集群并返回到初始状态。
- rabbitmqctl reset命令将清空节点的状态,并将其恢复到空白状态。当重设的节点是集群中的一部分时,该命令也会和集群中的磁盘节点进行通信,告诉它们该节点正在离开集群。不然集群会认为该节点出了故障,并期望其最终能够恢复过来。
1、第一种方法
- 将hh12节点从集群剔除(在hh14上执行)
//停止hh13上rabbitMQ服务 ]# rabbitmqctl -n rabbit@hh12 stop_app //rabbit@hh13节点从集群剔除 ]# rabbitmqctl forget_cluster_node rabbit@hh12
2、第二种方法
- 将hh13节点从集群剔除(在hh14上执行)
//停止RabbitMQ服务,但不停止Erlang VM。 rabbitmqctl --node rabbit@hh13 stop_app //使RabbitMQ节点脱离集群并返回到初始状态 rabbitmqctl --node rabbit@hh13 reset //启动RabbitMQ服务,但不运行Erlang VM rabbitmqctl --node rabbit@hh13 start_app
- 如果从hh13节点上检查集群的状态,会发现它现在是独立的节点。
1.3、关闭集群中的节点
- 现在己经完成了集群的搭建。如果关闭集群中的某个节点,集群会处于什么样的状态?
1、关闭一个节点
//关闭hh13上的RabbitMQ服务(在hh14上执行) ]# rabbitmqctl -n rabbit@hh13 stop_app //在hh14上查看集群状态,可以看到在running_nodes中已经没有rabbit@hh13这个节点了 ]# rabbitmqctl cluster_status ...... Running Nodes rabbit@hh12 rabbit@hh14
2、关闭所有节点
- 如果关闭了集群中的所有节点,则需要确保在启动的时候先启动最后关闭的那个节点。如果第一个启动的不是最后关闭的节点,那么这个节点会等待最后关闭的节点启动。如果等待是30秒还没有等到,那么这个先启动的节点就会启动失败。
- 在最新的版本中会有重试机制,默认重试10次30秒以等待最后关闭的节点启动。
//关闭所有节点的RabbitMQ服务 ]# rabbitmqctl stop_app //启动不是最后一个关闭的RabbitMQ服务 ]# rabbitmqctl start_app
- 如果最后一个关闭的节点由于某些异常无法启动,则可以通过rabbitrnqctl forget_cluster_node命令将此节点剔出集群。
- 如果集群中的所有节点由于某些非正常因素而关闭,比如断电,那么集群中的节点都会认为还有其他节点在它后面关闭,此时需要调用rabbitrnqctl force_boot命令来启动一个节点,之后集群才能正常启动
1.4、集群的节点类型
- RabbitMQ节点类型要么是内存节点,要么是磁盘节点。
- 内存节点将队列、交换器、绑定关系、用户、权限和vhost的元数据都存储在内存中。
- 磁盘节点将队列、交换器、绑定关系、用户、权限和vhost的元数据都存储在磁盘中。
- 有两个时机指定节点类型:
- (1)加入集群时指定节点类型。
- (2)集群搭建完成后,修改节点类型。
//加入集群时指定节点类型 rabbitmqctl [--node <node>] join_cluster [--disc|--ram] <existing_cluster_member> //集群搭建完成后,修改节点类型 rabbitmqctl [--node <node>] change_cluster_node_type <disc | ram>
- 单节点RabbitMQ的节点类型只能是磁盘节点,否则重启RabbitMQ之后,所有关于RabbitMQ的配置信息都会丢失。
- 多节点RabbitMQ集群中即可以有磁盘节点,也可以有内存节点。
- 在集群中创建队列、交换器或者绑定关系的时候,这些操作直到所有集群节点都成功提交元数据变更后才会返回。
- 内存节点可以提供出色的性能,磁盘节点能够保证集群配置信息的高可靠性。
- RabbitMQ集群至少要有一个磁盘节点(建议要有两个或者多个磁盘节点),其他所有节点都可以是内存节点。
- 如果只有一个磁盘节点,而且不凑巧的是它刚好崩溃了,那么集群可以继续发送或者接收消息,但是不能创建队列、交换器、绑定关系、用户、以及更改权限、添加或删除集群节点了。也就是说,如果集群中唯一的磁盘节点崩溃,集群仍然可以保持运行,但是直到将该节点恢复到集群前,无法更改任何东西。
- 当节点加入或者离开集群时,它们必须通知到至少一个磁盘节点。
- 在内存节点重启后,它们会连接到预先配置的磁盘节点,获取当前集群元数据的副本。
- 当在集群中添加内存节点时,确保告知其所有的磁盘节点(内存节点唯一存储到磁盘的元数据信息是集群中磁盘节点的地址)。只要内存节点可以找到至少一个磁盘节点,那么它就能在重启后重新加入集群中。
- 除非使用的是RabbitMQ的RPC功能,否则创建队列、交换器及绑定关系的操作确是甚少,大多数的操作就是生产或者消费消息。为了确保集群信息的可靠性,或者在不确定使用磁盘节点或者内存节点的时候,建议全部使用磁盘节点。
1、查看节点类型
- 使用rabbitmqctl cluster_status命令查看集群状态时,其中的Disk Nodes信息显示了Disk类型的RabbitMQ节点。
]# rabbitmqctl cluster_status ...... Disk Nodes rabbit@hh12 rabbit@hh13 rabbit@hh14
2、加入集群时指定节点类型
rabbitmqctl [--node <node>] join_cluster [--disc|--ram] <existing_cluster_member>
3、修改节点类型
- 集群搭建完成后,修改节点类型。
//修改hh14节点的节点类型 ]# rabbitmqctl stop_app ]# rabbitmqctl change_cluster_node_type ram ]# rabbitmqctl start_app
- 查看节点类型,可以看到hh14节点已经变成了RAM节点
]# rabbitmqctl cluster_status ...... Disk Nodes rabbit@hh12 rabbit@hh13 RAM Nodes rabbit@hh14
1.5、集群节点的升级
- 如果RabbitMQ集群由单独的一个节点组成,那么升级版本很容易:
- (1)关闭原来的服务
- (2)保存Mnesia数据
- (3)解压新版本到指定的目录
- (4)新版本使用原版的Mnesia数据
- (5)启动新版本的服务
- 如果RabbitMQ集群由多个节点组成,那么也可以参考单个节点的情形:
- (1)关闭所有节点的服务,注意采用rabbitmqctl stop命令关闭
- (2)保存各个节点的Mnesia数据
- (3)解压新版本到指定的目录
- (4)指定新版本使用原版的Mnesia数据。
- (5) 启动新版本的服务,注意先重启原版本中最后关闭的那个节点
- RabbitMQ的版本有很多,难免会有数据格式不兼容的现象,这个缺陷在越旧的版本中越发明显,所以在对不同版本升级的过程中,最好先测试两个版本的数据互通性,然后再在生产环境中操作。
- 如果原集群上的配置和数据都可以舍弃,则可以删除原版本的RabbitMQ,然后再重新安装配置即可。
- 如果原集群上的配置和数据不可以丢弃,则可以先保存元数据,之后再关闭所有生产者并等待消费者消费完队列中的所有数据,紧接着关闭所有消费者,然后重新安装RabbitMQ并重建元数据等。
- 当然如果有个新版本的集群,那么从旧版本迁移到新版本的集群中也不失为一个升级的好办法。
2、单点故障恢复
- 单点故障是指集群中单个节点发生了故障,有可能会引起集群服务不可用、数据丢失等异常。配置数据节点冗余(镜像队列)可以有效地防止由于单点故障而降低整个集群的可用性、可靠性
- 单节点故障包括:机器硬件故障、机器掉电、网络异常、服务进程异常。
1、机器硬件故障
- 机器硬件故障包括机器硬盘、内存、主板等故障造成的死机,无法从软件角度来恢复。此时需要将该节点从集群中剔除。
//从集群中移除故障节点(在其他节点中执行) rabbitmqctl [--node <node>] forget_cluster_node [--offline] <existing_cluster_member_node>
- 如果之前有客户端连接到此故障节点上,在故障发生时会有异常报出,此时需要将故障节点的IP地址从连接列表里删除,并让客户端重新与集群中的节点建立连接,以恢复整个应用。
- 如果此故障机器修复或者原本有备用机器,那么也可以选择性的添加到集群中。
2、机器掉电故障
- 机器掉电故障,需要等待电源接通之后重启机器。此时这个机器节点上的RabbitMQ处于stop状态,但是此时不要盲目重启服务,否则可能会引起网络分区。
//(1)从集群中移除故障节点(在其他节点中执行) rabbitmqctl [--node <node>] forget_cluster_node [--offline] <existing_cluster_member_node> //(2)删除当前故障机器中的RabbitMQ的Mnesia数据(相当于重置) //(3)重启RabbitMQ服务 //(4)再将此节点作为一个新的节点加入到当前集群中
3、网络异常
- 网线松动或者网卡损坏都会引起网络故障的发生。
- 对于网线松动,无论是彻底断开,还是"藕断丝连",只要它不降速,RabbitMQ集群就没有任何影响。但是为了保险起见,建议先关闭故障机器的RabbitMQ进程,然后对网线进行更换或者修复操作,之后再考虑是否重新开启RabbitMQ进程。
- 网卡故障极易引起网络分区的发生,如果监控到网卡故障而网络分区尚未发生时,理应第一时间关闭此机器节点上的RabbitMQ进程,在网卡修复之前不建议再次开启。如果己经发生了网络分区,可以手动恢复网络分区。
4、服务进程异常
- 对于服务进程异常,如RabbitMQ进程非预期终止,需要预先思考相关风险是否在可控范围之内。
- 如果风险不可控,可以选择抛弃这个节点。一般情况下,重新启动RabbitMQ服务进程即可。
3、集群迁移
- 对于RabbitMQ运维层面来说,扩容和迁移是必不可少的。
- 扩容比较简单,一般向集群中加入新的集群节点即可,不过新的机器节点中是没有队列创建的,只有后面新创建的队列才有可能进入这个新的节点中。或者如果集群配置了镜像队列,可以通过一点"小手术"将原先队列"漂移"到这个新的节点中。
- 迁移同样可以解决扩容的问题,将旧的集群中的数据(包括元数据信息和消息〉迁移到新的且容量更大的集群中即可。RabbitMQ中的集群迁移更多的是用来解决集群故障不可短时间内修复而将所有的数据、客户端连接等迁移到新的集群中,以确保服务的可用性。
- 相比于单点故障而言,集群故障的危害性就大得多,比如IDC整体停电、网线被挖断等。这时候就需要通过集群迁移重新建立起一个新的集群。
- RabbitMQ集群迁移包括元数据重建、数据迁移,以及客户端连接的切换。
3.1、元数据重建
- 元数据重建是指在新的集群中创建原集群的队列、交换器、绑定关系、vhost、用户、权限和Parameter等数据信息。元数据重建之后才可将原集群中的消息及客户端连接迁移过来。
- 重建元数据的方法有很多种:
- 手工或者使用客户端重建元数据,但这样需要对元数据进行整理(很费劲很麻烦)。如果事先没有统筹规划,通过人工的方式来完成这项工作是极其烦琐、低效的,且时效性太差,不到万不得已不建议使用。
- 使用Web管理界面重建元数据(这是最高效的手段)。
- 使用Web管理界面导出导入元数据如图所示:
- 在原集群上的Web管理界面中点击"Download broker definitions" 按钮下载集群的元数据信息文件,此文件是一个JSON文件。
- 在新集群上的Web管理界面中点击"Upload broker definitions" 按钮上传原集群的元数据信息文件。
- 如果新集群有数据与原集群的元数据信息文件中的数据相冲突,对于交换器、队列及绑定关系这类非可变对象而言会报错,而对于其他可变对象如Parameter、用户等则会被覆盖,没有发生冲突的则不受影响。如果导入过程中发生错误,导入就会终止,将导致只有部分数据加载成功。
- 使用Web管理界面导出导入元数据需要考虑三个问题:
- (1)如果原集群突发故障,又或者开启RabbitMQ Management插件的那个节点机器故障不可修复,就无法通过Web管理界面获取原集群的元数据了,这样元数据重建就无从谈起。这个问题也很好解决,可以采取一个通用的备份任务,在元数据有变更或者达到某个存储周期时将最新的元数据备份至另一处安全的地方。这样在遇到需要集群迁移时, 可以获取到最新的元数据。
- (2)如果新旧集群的RabbitMQ版本不一致时会出现异常情况,比如建立了一个3.6.1版本的新集群,旧集群版本为3.5.7,这两个版本的元数据就不相同。
- 一般情况下,RabbitMQ是能够做到向下兼容的,在高版本的RabbitMQ中可以上传低版本的元数据文件。然而如果在低版本中上传高版本的元数据文件就没有那么顺利了。
- (3)如果采用上面的方法将元数据在新集群上重建,则所有的队列都只会落到同一个集群节点上,而其他节点处于空置状态,这样所有的压力将会集中到这单台节点之上。
- 如果不能成功上传元数据,那么就需要进一步采取措施了。
- 对于用户、策略、权限这种元数据来说内容相对固定,且内容较少,手工重建的代价较小。
- 对于队列、交换器和绑定这三项最多且最复杂的内容,而且还涉及其内容的参数设置,如果采用人工重建的方式代价太大,重建元数据的意义其实就在于重建队列、交换器及绑定这三项的相关信息。
3.2、数据迁移和客户端连接的切换
- 首先需要将生产者的客户端与原RabbitMQ集群的连接断开,然后再与新的集群建立新的连接,这样就可以将新的消息流转入到新的集群中。
- 然后就需要考虑消费者客户端的事情,此时会有两种情况:
- (1)等待原集群中的消息全部消费完之后再将连接断开,然后与新集群建立连接进行消费作业。可以通过Web页面查看消息是否消费完成,也可以通过"rabbitmqctl list_queues name messages messages_ready messages_unacknowledged"命令来查看是否有未被消费的消息。
- (2)当原集群服务不可用或者出现故障造成服务质量下降而需要迅速将消息流切换到新的集群中时,此时就不能等待消费完原集群中的消息,这里需要及时将消费者客户端的连接切换到新的集群中,那么在原集群中就会残留部分未被消费的消息,此时需要做进一步的处理。如果原集群损坏,可以等待修复之后将数据迁移到新集群中,否则会丢失数据。
- 数据迁移的主要原理是先从原集群中将数据消费出来,然后存入一个缓存区中,另一个线程读取缓存区中的消息再发布到新的集群中, 如此便完成了数据迁移的动作。
- 作者将过程命名为"RabbitMQ ForwardMaker",读者可以自行编写一个小工具来实现这个功能。
- RabbitMQ本身提供的Federation和Shovel插件都可以实现"RabbitMQ ForwardMaker"的功能,确切地说Shovel插件更接近。
- 数据迁移原理图
3.3、自动化迁移
- 与生产者和消费者客户端相关的是交换器、队列及集群的信息,如果这3种类型的资源发生改变时需要让客户端迅速感知,以便进行相应的处理,则可以通过将相应的资源加载到ZooKeeper的相应节点中,然后在客户端为对应的资源节点加入watcher来感知变化,当然这个功能使用etcd5或者集成到公司层面的资源配置中心中会更加标准、高效。
- 如图所示,将整个RabbitMQ集群资源的使用分为三个部分:客户端、集群、ZooKeeper配置管理。
- 生产者在发送消息之前需要先连接ZooKeeper,然后根据指定的交换器名称如exchangel到相应的路径/rmqNode/exchanges中寻找exchangel,之后再读取exchangel中的数据,并同时对此exchangel添加watcher。在exchangel的数据第一条"cluster=clusterl"中找到交换器所在的集群名称,然后再从路径/rmqNode/clusters中寻找cluster1,然后读取其对应的IP地址列表信息。这样生产者所需要的连接串数据(IP地址列表、vhost、usemame、password等)都己获取,接下就可以与RabbitMQ集群clusterl建立连接然后发送数据了。
- 对于消费者客户端而言,同样需要连接ZooKeeper,之后根据指定的队列名称(queue1)到相应的路径/rmqNode/queues中寻找queue1,继而找到相应的连接串,然后与RabbitMQ集群cluster1建立连接进行消费。当然对/rmqNode/queues/queuel的watcher必不可少。
- 当clusterl集群需要迁移到cluster2集群时,首先需要将cluster1集群中的元数据在cluster2集群中重建。之后通过修改zk的channel和queue的元数据信息,比如原clusterl集群中有交换器exchangel、exchange2和队列queuel、queue2,现在通过脚本或者程序将其中的"cluster=cluster1"数据修改为"cluster=cluster2",客户端会立刻感知节点的变化,然后迅速关闭当前连接之后再与新集群cluster2建立新的连接后生产和消费消息,在此切换客户端连接的过程中是可以保证数据零丢失的。迁移之后,生产者和消费者都会与cluster2集群进行互通,此时原clusterl集群中可能还有未被消费完的数据,此时需要RabbitMQ ForwarMaker工具将clusterl集群中未被消费完的数据同步到cluster2集群中。
- 如果没有准备RabbitMQ ForwardMaker工具,也不想使用Federation或者Shovel插件,那么在变更完交换器相关的ZooKeeper中的节点数据之后,需要等待原集群中的所有队列都消费完全之后,再将队列相关的ZooKeeper中的节点数据变更,进而使得消费者的连接能够顺利迁移到新的集群之上。
- 上面的自动化迁移立足于将现有集群迁移到空闲的备份集群,如果由于原集群硬件升级等原因迁移也无可厚非。很多情况下,自动化迁移作为容灾手段中的一种,如果有很多个正在运行的RabbitMQ集群,为每个集群都配备一个空闲的备份集群无疑是一种资源的浪费。
- 如果有很多个正在运行的RabbitMQ集群,可以让多个RabbitMQ集群相互备份。
- 在正常情况下,客户端只用自己RabbitMQ集群,不使用备份的RabbitMQ集群,即RabbitMQ集群只是记录了该有的元数据而已,不会消耗备份集群的性能。
- 多个RabbitMQ集群相互备份
4、集群监控
- 任何应用功能再强大、性能再优越,如果没有与之匹配的监控那么一切都是虚无飘渺的。监控不仅可以提供运行时的数据为应用提供依据参考,还可以迅速定位问题、提供预防及告警等功能,很大程度上增强了整体服务的可用性。
- RabbitMQ扩展的RabbitMQ Management插件就能提供一定的监控功能,Web管理界面提供了很多的统计值信息:如发送速度、确认速度、消费速度、消息总数、磁盘读写速度、句柄数、Socket连接数、Connection数、Channel数、内存信息等。
- 总体上来说,RabbitMQ Management插件提供的监控页面是相对完善的,在实际应用中具有很高的使用价值。
- 但是难以和公司内部系统平台关联,对于业务资源的使用情况、相应的预防及告警的联动无法顺利贯通。如果在人力、物力等条件允许的情况下,自定义一套监控系统非常有必要。
4.1、通过HTTP API接口提供监控数据
- 可以通HTTP API说明文档获取更多信息(http://IP:15672/api/index.html)。
//获取vhost列表 curl -i -u guest:guest http://localhost:15672/api/vhosts //创建一个虚拟主机foo curl -i -u guest:guest -H "content-type:application/json" -XPUT http://localhost:15672/api/vhosts/foo //创建一个exchange(%2F是默认vhost "/") curl -i -u guest:guest -H "content-type:application/json" -XPUT -d'{"type":"direct","durable":true}' http://localhost:15672/api/exchanges/%2F/my-new-exchange //删除一个exchange curl -i -u guest:guest -H "content-type:application/json" -XDELETE http://localhost:15672/api/exchanges/%2F/my-new-exchange
4.2、检测RabbitMQ服务是否健康
- 可以通HTTP API说明文档获取更多信息检测RabbitMQ服务是否健康的URL(http://IP:15672/api/index.html)
- 不管是通过HTTP API接口还是客户端,获取的数据都是以作监控视图之用,不过这一切都基于RabbitMQ服务运行完好的情况下。虽然可以通过某些其他工具或方法来检测RabbitMQ进程是否在运行(如ps aux | grep rabbitmq),或者5672端口是否开启(如telnet xxx.xxx.xxx.xxx 5672),但是这样依旧不能真正地评判RabbitMQ是否还具备服务外部请求的能力。
- RabbitMQ Management插件提供了/api/aliveness-test/VHOST_NAME接口,这个接口通过3个步骤来验证RabbitMQ服务的健康性:
- (1)创建一个队列"aliveness-test"来接收测试消息。
- (2)用队列名称"aliveness-test"作为消息的路由键,将消息发往默认交换器。
- (3)到达队列时就消费该消息,否则就报错。
- 如果RabbitMQ服务完好,调用/api/aliveness-test/VHOST_NAME接口会返回{"status": "ok"},HTTP状态码为200。
示例:
//创建一个虚拟直接vhost_test ]# curl -u root:root -H "content-type:application/json" -XPUT http://10.1.1.14:15672/api/vhosts/vhost_test //检测RabbitMQ服务是否健康 ]# curl -u root:root http://10.1.1.14:15672/api/aliveness-test/vhost_test {"status":"ok"}
4.3、元数据管理与监控
- 确保RabbitMQ能够健康运行还不足以让人放松警惕。
- 在实际生产环境中,如果误删了一个队列,必然会造成不可估计的影响。此时业务方如果正在使用这个队列,正常情况下会立刻报出异常,相关人员可以迅速做出动作以尽可能地降低影响。试想如果是一个定时任务调用此队列,并在深夜3点执行相应的逻辑,此时报出异常想必也会对相关人员造成不小的精神骚扰。
- 不止删除队列这一个方面,还有删除了一个交换器,或者修改了绑定信息,再或者是胡乱建立了一个队列绑定到现有的一个交换器中,同时又没有消费者订阅消费此队列,从而留下消息堆积的隐患等都会对使用RabbitMQ服务的业务应用造成影响。所以对于RabbitMQ元数据的管理与监控也尤为重要。
- 许多应用场景是在业务逻辑代码中创建相应的元数据资源(交换器、队列及绑定关系)并使用。
- 对于排他的、自动删除的这类非高可靠性要求的元数据资源可以在一定程度上忽略元数据变更的影响。
- 但是对于两个非常重要的且通过消息中间件交互的业务应用,在使用相应的元数据资源时最好进行相应的管控,如果一方或者其他方肆意变更所使用的元数据,必然对另一方造成不小的损失。
- 管控的介入自然会降低消息中间件的灵活度,但是可以增强系统的可靠性。比如通过专用的"元数据审核系统"来配置相应的元数据资源,提供给业务方使用的用户只有可读和可写的权限,这样可以进一步降低风险。
- 非管控的元数据可以天马行空,业务方可以在这一时刻创建,下一时刻就删除,对其监控也无太大的意义。
- 对于管控的元数据来说,监控的介入就会有意义也会有必要很多。虽然对于只有可读写权限的用户不能够变更元数据信息,也难免会被其他具有可配置权限的超级用户篡改。
- RabbitMQ中在创建元数据资源的时候是以一种声明的形式完成的:无则创建、有则不变,不过在对应的元数据存在的情况下,对其再次声明时使用不同的属性会报出相应的错误信息。
- 如图7-15所示,所有的业务应用都需要通过元数据审核系统来申请创建(当然也可以包含查询、修改及删除)相应的元数据信息。在申请动作完成之后,由专门的人员进行审批,之后在数据库中存储和在RabbitMQ集群中创建相应的元数据,这两个步骤可以同时进行,而且也无须为这两个动作添加强一致性的事务逻辑。在数据库和RabbitMQ集群之间会有一个元数据一致性校验程序来检测元数据不一致的地方,然后将不一致的数据上送到监控管理系统。监控管理系统中可以显示元数据不一致的记录信息,也可以以告警的形式推送出来,然后相应的管理人员可以选择手动或者自动地进行元数据修正。这里的不一致有可能是由于数据库的记录未被正确及时地更新,也有可能是RabbitMQ集群中元数据被异常篡改。元数据修正需慎之又慎,在整个系统修正逻辑完备之前,建议优先采用人工的方式,毕竟不一致的元数据仅占少数,人工修正的工作量并不太大。
- RabbitMQ的元数据可以很顺利地以表的形式记录在数据库中,主要的元数据是queues、exchanges和bindings,可以分别建立三张表。
- rmq_queues表:队列信息表。列名有name、vhost、durable、auto_delete、arguments、cluster_name、description。
- cluster_name:队列所在的集群名称,毕竟一般一个公司所用的RabbitMQ集群不会只有一个。
- description:相应的描述信息,相当于备注,通常可置为空。
- rmq_exchanges表:交换器信息表。列名有name、vhost、type、durable、auto_delete、internal、arguments、cluster_name、description。
- rmq_bindings表:绑定信息表。列名有source、vhost、destination、destination_type、routing_key、arguments、cluster_name、description。
- rmq_queues表:队列信息表。列名有name、vhost、durable、auto_delete、arguments、cluster_name、description。
- 元数据一致性检测程序可以通过/api/definitions的HTTP API接口获取集群的元数据信息,通过解析之后与数据库中的记录一一比对,查看是否有不一致的地方。
# #