龙空技术网

RabbitMQ学习(二):高可靠之持久化与高可用之镜像队列

服务端技术 330

前言:

此刻咱们对“java 线程池队列持久化”大致比较着重,朋友们都需要了解一些“java 线程池队列持久化”的相关内容。那么小编也在网络上汇集了一些有关“java 线程池队列持久化””的相关资讯,希望姐妹们能喜欢,大家快快来学习一下吧!

概述

RabbitMQ是对内存队列,如Java的阻塞队列BlockingQueue,的一种升级,即作为一个进程队列实现不同进程之间的消息通信交互,而内存队列,如BlockingQueue则通常用于实现一个Java进程的不同线程之间的消息通信交互。这也是顺应从单体应用到分布式系统的演变所必须的消息队列的演进,解决了分布式系统不同系统之间的消息传递问题。由于RabbitMQ主要用于实现不同进程或者说不同系统之间的消息传递,与内存队列在进程重启自动销毁类似,RabbitMQ的相关组件,如交换器,队列,消息等默认情况下也是存放在内存中的,当RabbitMQ服务器重启时,这些组件相关的元数据会丢失,重启时需要重新创建这些核心组件。这样设计的合理之处在于RabbitMQ并不是一个数据存储系统,而是相对于内存队列,提供了通过网络的方式为不同系统进行消息传递,这也是AMQP协议的核心所在。不过在实际应用中,由于组成分布式系统的各个子系统的功能通常是固定的,如电商网站中的账号系统,订单系统,物流系统等,当选择RabbitMQ作为这些子系统的消息队列时,通常需要保证RabbitMQ在意外宕机或者重启后,相关的交换器,队列,未消费的消息还存在,而不需要重新创建交换器或者队列或者未消费的消息不会丢失,这样才能减少对生产者和消费者,如以上的电商系统的相关业务子系统,的影响。所以需要对交换器,队列和队列的消息进行持久化处理,即持久化到磁盘中,当RabbitMQ宕机或者重启时,可以从磁盘将这些组件的数据重新加载到内存中。

持久化:高可靠

1. 交换器与队列持久化

交换器和队列的持久化主要是在创建交换器或者队列时,通过设置durable参数为true来告诉RabbitMQ对这个所创建的交换器或者队列进行持久化处理。通常需要对交换器和队列同时设置持久化,这样能保证不只是交换器和队列在RabbitMQ重启时还存在,交换器和队列之间的绑定关系Binding也还存在,这样对生产者和消费者影响最小,即生产者和消费者可以继续正常生产数据或者消费数据,就像RabbitMQ没有重启过一样。交换器和队列的持久化主要是针对交换器和队列的相关元数据的持久化,将这些元数据持久化到磁盘中。对于交换器而言,需要持久化的元数据主要包括队列的名字,队列类型,如faout,direct,topic或者headers,以及其他相关属性,如持久化属性durable。交换器持久化之后,当RabbitMQ重启后,生产者可以继续使用这个交换器来传递数据,而无需在手动创建这个交换器,这样能尽可能地降低对生产者的影响。对于队列而言,需要持久化的元数据主要为队列的名字,以及属性,如是否持久化durable,是否自动删除。不过这里需要注意的是,如果一个队列是排他队列,即只对当前的连接Connection有效,只能被当前连接Connection的多个Channel访问,则即使设置了该队列为持久队列,当这个连接Connection断开时,该队列也会自动删除掉,而不会再持久化到磁盘中。除了需要持久化交换器和队列自身的元数据外,交换器和队列之间的绑定映射关系也会持久化。在磁盘存储方面,交换器和队列的持久化信息通常存放在rabbitmq服务器的/var/lib/rabbitmq/mnesia/目录下面。

2. 消息持久化

以上的分析的交换器和队列的持久化只是针对交换器和队列自身元数据的持久化,而不会对消息进行持久化,即消息还是存放在RabbitMQ服务器的内存中的,如果RabbitMQ服务器宕机或者重启,队列中的消息会丢失。所以如果要保证投递到队列中的消息在RabbitMQ服务器宕机重启时不会丢失,需要进行消息持久化。消息持久化主要是在生产者控制的。即生产者在创建消息时,可以设置消息的属性properties的deliveryMode为2来指定这个消息是需要持久化的。当将该消息投递到RabbitMQ服务器的队列之后,需要首先写到磁盘中,然后再在内存队列保留这条消息,最后如果生产者需要确认ACK,则再回调通知生产者这条消息投递成功。由于消息写入磁盘是随机写操作,性能较低,会一定程度影响RabbitMQ服务器整体的吞吐量,所以一般对重要,对消息丢失容忍度低的场景的消息,才将该消息设置为持久化。还有就是当对磁盘文件写入时,操作系统不是对每次写入都直接写到磁盘文件的,而是会写到操作系统的页缓存中,等之后再刷到磁盘,所以如果中间,机器宕机了,则即使设置了消息持久化,也可能造成消息丢失。

镜像队列:高可用

以上的消息持久化机制只能保证在该机器不宕机,磁盘不损坏的情况下的数据可靠性,并没有实现消息在其他机器的冗余存储,即RabbitMQ的消息默认是只在其所被投递到的某个机器的RabbitMQ服务器的某个队列中存放一份,在其他队列或者其他机器的队列并没有一份拷贝,所以缺乏高可用特性。所以为了实现高可用,RabbitMQ提供了镜像队列机制。所谓镜像队列其实就是在另外一个RabbitMQ服务器Broker存放一个该队列的一个拷贝队列,实现队列内消息的冗余存储。镜像队列机制是基于Master-Slave主从模式的:针对这个队列的所有操作都是只能在Master节点进行操作的,包括生产者发布消息到队列,分发消息给消费者,跟踪消费者的消费确认ACK等,然后将这些操作对应的消息由Master节点广播同步给其他节点。针对消息消费,与MySQL和Redis的主从模式中master负责写请求,slave负责读请求实现读写分离不同的是,在RabbitMQ的镜像队列模式中,消费者是从Master节点消费数据的,即不管消费者连接的是哪个节点进行消费,如连接到mirro节点或者说是slave节点消费,slave节点会将消费请求转发到master进行消费。当成功消费并确认ACK删除该消息时,由Master节点同步这个删除信息给其他Slave节点。所以镜像队列解决的是消息高可用问题,而不是基于负载均衡实现的高吞吐量问题。高吞吐量主要是通过RabbitMQ集群来实现的,即在不同节点创建不同队列来提高消息处理能力和吞吐量。消费者默认不会从Slave节点消费数据,只有当Master节点宕机,Slave节点升级为Master节点时才会消费这个节点的数据。Master节点宕机时,会从所有slave节点中选择最早加入这个镜像队列集合的slave作为新的master,即根据加入时间来判断的。

标签: #java 线程池队列持久化