RabbitMQ–扩展–03–日志文件,故障恢复,集群迁移,集群监控
1、RabbitMQ日志查看
如果在使用RabbitMQ 的过程中出现了异常情况,通过查看RabbitMQ 的服务日志可以让你在处理异常的过程中事半功倍。
RabbitMQ 日志中包含各种类型的事件,比如连接尝试、服务启动、插件安装及解析请求时的错误等。
1.1、日志文件
1.1.1、默认路径
/var/log/rabbitmq
2、RabbitMQ故障恢复
在RabbitMQ 使用过程中,或多或少都会遇到一些故障。对于集群层面来说,更多的是单点故障。
所谓的单点故障是指集群中单个节点发生了故障,有可能会引起集群服务不可用、数据丢失等异常。配置数据节点冗余(镜像队列)可以有效地防止由于单点故障而降低整个集群的可用性、可靠性,本节主要讨论的是单节点故障有哪些,以及怎么恢复或者处理相应类型的单节点故障。
2.1、单节点故障包括
- 机器硬件故障
- 机器掉电
- 网络异常
- 服务进程异常。
2.1.1、机器硬件故障
包括机器硬盘、内存、主板等故障造成的死机,无法从软件角度来恢复。
此时需要在集群中的其他节点中执行rabbitmqctl forget cluster node{nodename} 命令来将故障节点剔除,其中nodename 表示故障机器节点名称。
如果之前有客户端连接到此故障节点上,在故障发生时会有异常报出,此时需要将故障节点的IP地址从连接列表里删除,并让客户端重新与集群中的节点建立连接,以恢复整个应用。如果此故障机器修复或者原本有备用机器,那么也可以选择性的添加到集群中。
2.1.2、机器掉电
需要等待电源接通之后重启机器。此时这个机器节点上的RabbitMQ处于stop状态,但是此时不要盲目重启服务,否则可能会引起网络分区。
此时同样需要在其他节点上执行rabbitmqctl forget_cluster_node{nodename} 命令将此节点从集群中剔除,然后删除当前故障机器的RabbitMQ 中的Mnesia数据(相当于重置),然后再重启RabbitMQ 服务,最后再将此节点作为一个新的节点加入到当前集群中。
2.1.3、网络异常
网线松动或者网卡损坏都会引起网络故障的发生。
对于网线松动,无论是彻底断开,还是"藕断丝连",只要它不降速, RabbitMQ 集群就没有任何影响。但是为了保险起见,建议先关闭故障机器的RabbitMQ 进程,然后对网线进行更换或者修复操作,之后再考虑是否重新开启RabbitMQ 进程。而网卡故障极易引起网络分区的发生,如果监控到网卡故障而网络分区尚未发生时,理应第一时间关闭此机器节点上的RabbitMQ 进程,在网卡修复之前不建议再次开启。
2.1.4、服务进程异常
如RabbitMQ 进程非预期终止,需要预先思考相关风险是否在可控范围之内。如果风险不可控,可以选择抛弃这个节点。
一般情况下,重新启动RabbitMQ 服务进程即可。
3、RabbitMQ集群迁移
对于RabbitMQ 运维层面来说,扩容和迁移是必不可少的。扩容比较简单,一般向集群中加入新的集群节点即可
不过新的机器节点中是没有队列创建的,只有后面新创建的队列才有可能进入这个新的节点中。或者如果集群配置了镜像队列,可以通过一点"小手术"将原先队列"漂移"到这个新的节点中。
迁移同样可以解决扩容的问题,将旧的集群中的数据(包括元数据信息和消息)迁移到新的且容量更大的集群中即可。
RabbitMQ 中的集群迁移更多的是用来解决集群故障不可短时间内修复而将所有的数据、客户端连接等迁移到新的集群中,以确保服务的可用性。相比于单点故障而言,集群故障的危害性就大得多,比如IDC 整体停电、网线被挖断等。这时候就需要通过集群迁移重新建立起一个新的集群。
RabbitMQ 集群迁移包括元数据重建、数据迁移,以及与客户端连接的切换。
4、RabbitMQ集群监控
任何应用功能再强大、性能再优越,如果没有与之匹配的监控那么一切都是虚无缭绕妙的。
监控不仅可以提供运行时的数据为应用提供依据参考,还可以迅速定位问题、提供预防及告警等功能,很大程度上增强了整体服务的鲁棒性。
RabbitMQ 扩展的RabbitMQ Management 插件就能提供一定的监控功能。Web 管理界面提供了很多的统计值信息: 如发送速度、确认速度、消费速度、消息总数、磁盘读写速度、句柄数、Socket 连接数、Connection 数、Channel数、内存信息等。
总体上来说,RabbitMQ Management 插件提供的监控页面是相对完善的,在实际应用中具有很高的使用价值。但是有一个遗憾就是其难以和公司内部系统平台关联,对于业务资源的使用情况、相应的预防及告警的联动无法顺利贯通。如果在人力、物力等条件允许的情况下,自定义一套监控系统非常有必要。
4.1、通过 HTTP API接口 提供监控数据
- RabbitMQ 提供了 HTTP API接口 查看监控数据。
- 下面以集群、交换器和队列 3个角度来阐述如何通过HTTP API 获取监控数据。
- 假设集群中一共有4个节点node1、node2、node3、node4,有一个交换器exchange通过一个路由键"rk" 绑定了3个队列queue1、queue2、queue3
4.1.1、从数据采集到用户使用的过程
下面首先收集集群节点的信息,集群节点的信息可以通过/api/nodes接口来获取。
- 首先采集程序通过定时调用HTTPAPI 接口获取JSON 数据
- 然后进行JSON 解析之后再进行持久化处理。
- 对于这种基于时间序列的数据非常适合使用OpenTSDB来进行存储
- 监控管理系统可以根据用户的检索条件来从OpenTSDB中获取相应的数据并展示到页面之中。
4.1.1.1、OpenTSDB
- 基于Hbase 的分布式的,可伸缩的时间序列数据库。
- 主要用途就是做监控系统,比如收集大规模集群的监控数据并进行存储、查询,包括
- 网络设备
- 操作系统
- 应用程序
4.1.1.2、监控管理系统
- 具备报表、权限管理等功能
- 可以实时读取所采集的数据,对其进行分析处理,对于异常的数据需要及时报告给相应的人员等功能
4.1.2、交换器的数据采集接口
/api/exchanges/vhost/name
4.1.3、队列的数据采集接口
/api/queues/vhost/name
4.2、通过客户端提供监控数据
Java版 客户端中Channel接口中也提供了两个方法来获取数据。定义如下:
/**
* Returns the number of messages i 口a queue ready to be delivered
* to consumers. This method assumes the queue exists . If it doesn ' t ,
* an exception will be closed with an exception .
* @param queue the 口ame of the queue
* @return the number of messages in ready state
* @throws IOException Problem transmitting method.
*/
long messageCount(String queue)throws IOException;
/**
* Returns the number of consumers on a queue .
* This method assumes the queue ex 工sts. If it doesn't ,
* an except 工on wil1 be closed with an exception.
* @param queue the name of the queue
* @return the number of consumers
* @throws IOException Problem transmitting method .
*/
long consumerCount(String queue)throws IOException;
messageCount (String queue)用来查询队列中的消息个数,可以为监控消息堆积的情况提供数据。consumerCount(String queue)用来查询队列中的消费者个数,可以为监控消费者的情况提供数据。
除了这两个方法,也可以通过连接的状态进行监控。
Java 客户端中Connection 接口提供了addBlockedListenerCBlockedListenerlistener)方法(用来监昕连接阻塞信息)和addShutdownListener CShutdownListener listener)方法(用来监昕连接关闭信息)
try {
Connection connection = connectionFactory.newConnection();
connection.addShutdownListener(new ShutdownListener(){
public void shutdownCompleted(ShutdownSignalException cause){
//处理并记录连接关闭事项
}
});
connection.addBlockedListener(new BlockedListener(){
public void handleBlocked(String reason)throws IOException {
//处理并记录连接阻塞事项
}
public void handleUnblocked()throws IOException {
//处理并记录连接阻塞取消事项
}
});
Channel channel = connection.createChannel();
long msgCount = channel.messageCount(" queuel ");
long consumerCount = channel.consumerCount("queuel");
//记录msgCount 和consumerCount
} catch (Exception e){
e.printStackTrace();
} catch (TimeoutException e){
e.printStackTrace();
}
用户客户端还可以自行定义一些数据进行埋点,比如客户端成功发送的消息个数和发送失败的消息个数,进一步可以计算发送消息的成功率等。
4.3、检测RabbitMQ 服务是否健康
不管是通过HTTP API 接口还是客户端,获取的数据都是以作监控视图之用,不过这一切都基于RabbitMQ 服务运行完好的情况下。虽然可以通过某些其他工具或方法来检测RabbitMQ进程是否在运行(如ps aux I grep rabbitmq),或者5672 端口是否开启(如telnet xxx.xxx.xxx.xxx 5672),但是这样依旧不能真正地评判RabbitMQ 是否还具备服务外部请求的能力。
这里就需要使用AMQP 协议来构建一个类似于TCP 协议中的Ping 的检测程序。当这个测试程序与RabbitMQ 服务无法建立TCP 协议层面的连接,或者无法构建AMQP 协议层面的连接,再或者构建连接超时时,则可判定RabbitMQ 服务处于异常状态而无法正常为外部应用提供相应的服务。
4.4、元数据管理与监控
确保RabbitMQ 能够健康运行还不足以让人放松警惕。考虑这样一种情况:小明为小张创建了一个队列并绑定了一个交换器,之后某人由于疏忽而阴差阳错地删除了这个队列而无人得知,最后小张在使用这个队列的时候就会报出"NOT FOUND" 的错误。
如果这些在测试环境中发生,那么还可以弥补。在实际生产环境中,如果误删了一个队列,必然会造成不可估计的影响。此时业务方如果正在使用这个队列,正常情况下会立刻报出异常,相关人员可以迅速做出动作以尽可能地降低影响。试想如果是一个定时任务调用此队列,并在深夜3点执行相应的逻辑,此时报出异常想必也会对相关人员造成不小的精神骚扰。
不止删除队列这一个方面,还有删除了一个交换器,或者修改了绑定信息,再或者是胡乱建立了一个队列绑定到现有的一个交换器中,同时又没有消费者订阅消费此队列,从而留下消息堆积的隐患等都会对使用RabbitMQ服务的业务应用造成影响。所以对于RabbitMQ 元数据的管理与监控也尤为重要。
许多应用场景是在业务逻辑代码中创建相应的元数据资源(交换器、队列及绑定关系)并使用。对于排他的、自动删除的这类非高可靠性要求的元数据资源可以在一定程度上忽略元数据变更的影响。但是对于两个非常重要的且通过消息中间件交互的业务应用,在使用相应的元数据资源时最好进行相应的管控,如果一方或者其他方肆意变更所使用的元数据,必然对另一方造成不小的损失。管控的介入自然会降低消息中间件的灵活度,但是可以增强系统的可靠性。比如通过专用的"元数据审核系统"来配置相应的元数据资源,提供给业务方使用的用户只有可读和可写的权限,这样可以进一步降低风险。
非管控的元数据可以天马行空,业务方可以在这一时刻创建,下一时刻就删除,对其监控也无太大的意义。对于管控的元数据来说,监控的介入就会有意义也会有必要很多。虽然对于只有可读写权限的用户不能够变更元数据信息,也难免会被其他具有可配置权限的超级用户篡改。RabbitMQ 中在创建元数据资源的时候是以一种声明的形式完成的:无则创建、有则不变,不过在对应的元数据存在的情况下,对其再次声明时使用不同的属性会报出相应的错误信息。我们可以利用这一特性来监控元数据的变更,通过定时程序来将记录中的元数据信息重新声明一次,查看是否有异常报出。不过这种方法非常具有局限性,只能增加元数据的信息而不能减少。比如有一个队列没有消费者且以后也不会被使用,我们对其进行了解绑操作,这样就没有更多的消息流入而造成消息堆积,不过这一变更由于某些局限性没有及时将记录变更以通知到那个定时程序,此时又重新将此队列绑定到原交换器中。
4.4.1、示例
这里列举一个简单的元数据管控和监控的示例来应对此种情况,此系统并非最优,但可以给读者在实际应用时提供一种解决对应问题的思路。
如图所示,所有的业务应用都需要通过元数据审核系统来申请创建(当然也可以包含查询、修改及删除〉相应的元数据信息。在申请动作完成之后,由专门的人员进行审批,之后在数据库中存储和在RabbitMQ 集群中创建相应的元数据,这两个步骤可以同时进行,而且也无须为这两个动作添加强一致性的事务逻辑。
在数据库和RabbitMQ 集群之间会有一个元数据一致性校验程序来检测元数据不一致的地方,然后将不一致的数据上送到监控管理系统。
监控管理系统中可以显示元数据不一致的记录信息,也可以以告警的形式推送出来,然后相应的管理人员可以选择手动或者自动地进行元数据修正。这里的不一致有可能是由于数据库的记录未被正确及时地更新,也有可能是RabbitMQ 集群中元数据被异常篡改。元数据修正需慎之又慎,在整个系统修正逻辑完备之前,建议优先采用人工的方式,毕竟不一致的元数据仅占少数,人工修正的工作量并不太大。
RabbitMQ 的元数据可以很顺利地以表的形式记录在数据库中,参考附录A,主要的元数据是queues、exchanges 和bindings,可以分别建立三张表。
Table 1: 队列信息表,名称为rmq queues 。
列名有name、vhost、durable、auto delete、arguments、cluster name、description,其中name、durable、auto delete、arguments
Table 2: 交换器u信息表,名称为rmq_exchanges
列名有name、vhost、type、durable、auto delete,internal,arguments、cluster name、description 。其中name、type、durable、auto delete、internal、arguments
Table 3: 绑定信息表,名称为rmq bindings
列名有source、vhost、destination、estination_type、routing_key、arguments、cluster name、descriptio
元数据一致性检测程序可以通过/api/definitions 的HTTP API 接口获取集群的元数据信息,通过解析之后与数据库中的记录一一比对,查看是否有不一致的地方。