RabbitMQ实战:可用性分析和实现

本系列是「RabbitMQ实战:高效部署分布式消息队列」书籍的总结笔记。安全

上一篇介绍了各类场景下的最佳实践,大部分场景可使用「发后即忘」的模式,不须要响应,若是须要响应,可使用RabbitMQ的RPC模型。服务器

RabbitMQ以异步的方式解耦系统间的关系,调用者将业务请求发送到Rabbit服务器,就能够返回了,Rabbit会确保请求被正确处理,即便遇到网络异常、Rabbit服务器崩溃、整个机房断电等特殊场景,针对这些场景,Rabbit提供了各类机制确保其可用性。微信

本篇经过总结可能出现的特殊场景,对Rabbit提供的可用性保证进行分析,学习它的实现方式,你会了解到:网络

  • 总结异常场景
  • 集群并处理失败
  • 链接丢失和故障转移
  • 主/备方式
  • 跨机房复制

推广下个人我的公众号「情情说」,第一时间分享个人工做、学习和生活,若是对你有帮助,但愿能够关注下。架构

异常场景

在实际工做中,有很大一部分时间用在解决各类异常状况,好比针对用户输入的验证,JDK中提供的各类异常类,网络异常等,这些相对来讲比较好解决。负载均衡

Rabbit服务做为调用者和处理者的桥梁,相当重要,若是由于网络异常、单台服务器崩溃、机房瘫痪等缘由致使Rabbit服务不可用,会影响全部依赖的业务系统。异步

网络异常

处理者和服务端是经过长链接交互的,这样能够将消息实时推送,网络异常可能会致使长链接断开,若是客户端没法感知,处理者将接收不到任何消息,这种状况称为「链接丢失」。分布式

经过捕获链接异常,进行重连,能够解决这种问题,另外,Rabbit客户端进行了封装,很容易处理这种问题。性能

服务器崩溃

若是只有一台服务器服务,服务器崩溃将致使服务不可用,通常会使用集群将多个服务器当作一个总体对外提供服务,这样,单台服务器崩溃不会影响总体的服务。学习

使用集群后,就要考虑一些问题:

  • 客户端链接到哪台服务器是随机的,而一个队列只会在某个服务器中,因此,每台服务器都要保存队列元数据(相似索引),而且可从其余服务器获取实际的队列数据;
  • 服务器崩溃,会致使非持久化的队列、交换器丢失,客户端端重连后,要再次进行建立,但未消费的消息将没法恢复;
  • 若是队列、交换器、消息等是持久化的,如何进行恢复呢,Rabbit提供了几种方式进行处理,后面会详细介绍;
  • 订阅者也须要从新创建链接,进行监听;
机房瘫痪

若是考虑机房瘫痪,就要建多个数据中心,RabbitMQ提供了一种机制,能够方便地在不一样数据中心的Rabbit间复制消息。

集群并处理失败

RabbitMQ最优秀的功能之一就是其内建集群,主要用于完成2个目标:

  • 容许消费者和生产者在Rabbit节点崩溃的状况下继续运行;
  • 经过添加更多的节点线性扩展消息通讯的吞吐量;
集群架构

RabbitMQ会始终记录四种类型的内部元数据(相似索引):

  • 队列元数据:队列名称和它的属性;
  • 交换器元数据:交换器名称、类型和属性;
  • 绑定元数据:一张简单的表格展现了如何将消息路由到队列;
  • vhost元数据:为vhost内的队列、交换器和绑定提供命名空间和安全属性;

当引入集群时,就须要追踪新的元数据类型:集群节点位置,以及节点与已记录的其余类型元数据的关系。

不是每一个节点都有全部队列的彻底拷贝,若是在集群中建立队列,只会在单个节点上建立完整的队列信息(元数据、状态、内容),全部其余节点只知道队列的元数据和指向该队列的节点指针。

若是节点崩溃了,附加在队列上的消费者也就没法接收新的消息了。可让消费者重连到集群并从新建立队列,这种作法仅当队列没设置持久化时才可行,这是为了确保当失败的节点恢复后加入集群,节点上的队列消息不会丢失。

为何不将队列内容和状态复制到全部节点:第一,存储空间,若是每一个集群节点都拥有全部队列的彻底拷贝,添加新节点不会带来更多存储空间;第二,性能,消息的发布者须要将消息复制到每个集群节点,对于持久化消息,网络和磁盘复制都会增长。

而交换器只是一张查询表,而非实际的消息路由器,所以将交换器在整个集群中进行复制会更加简单

能够把每一个队列想象成节点上运行的进程,每一个进程拥有本身的进程ID,交换器只是路由模式列表和匹配消息应发往的队列进程ID列表。

集群架构中的交换器和队列

每一个Rabbit节点,要么是内存节点,要么是磁盘节点,单节点系统只运行磁盘类型的节点,在集群中,能够选择配置部分节点为内存节点。

在集群中声明队列、交换器或绑定的时候,这些操做直到全部集群节点都成功提交元数据变动后才返回。

RabbitMQ只要求集群中至少有一个磁盘节点,若是只有一个磁盘节点,恰好又崩溃了,集群能够继续路由消息,但不能建立队列、交换器、绑定、添加用户、更改权限等操做。因此,建议设置两个磁盘节点,当内存节点重启后,会链接到预先配置的磁盘节点,下载当前集群元数据拷贝,因此要将全部磁盘节点告诉内存节点。

镜像队列

前面提到,队列只会在集群中的一个节点,节点崩溃后,队列消息就会丢失,RabbitMQ2.6版本以后,提供了镜像队列,一旦主队列不可用,从队列将被选举为新的主队列。

对于镜像队列,除了将消息按照路由绑定规则投递到合适的队列,也会将消息投递到镜像队列的从拷贝。

对于发送方确认消息,Rabbit会在全部队列和队列的从拷贝安全地接收到消息时,才会通知发送方。

另外,使用镜像队列时,有一个问题:若是主拷贝节点发送故障,从队列会选举Wie主队列,全部该队列的消费者须要从新附加并监听新的队列主拷贝。对于经过故障节点进行链接的消费者,能够经过丢失到节点的TCP链接检测到,但对于那些经过节点附加到镜像队列且正常运行的消费者将没法检测到。

Rabbit经过给消费者发送一个消费者取消通知,告知再也不附加在队列主拷贝了,须要从新链接。

链接丢失和故障转移

这一小节主要讨论消费者如何检测链接丢失,并进行重连操做。

处理到集群的重连有多重策略,比较好的一种方式是使用负载均衡,不只能够减小应用程序处理节点故障代码的复杂性,又能确保在集群中链接的平均分配。

关于负载均衡,网上介绍的比较多了,这里就再也不过多介绍了,主要看看如何感知故障,并进行重连操做。

感知故障比较简单,当长链接断开时,会抛出异常,捕获对应的异常便可。

当集群节点出现故障时,应用程序须要考虑:下一个该连向哪里?这个工做已经交由负载均衡器决定。

关于重连处理,要考虑:

  • 若是重连到新的服务器,信道以及其上的全部消费循环都会失效,须要对他们进行重建;
  • 当进行重连时,全部的队列、绑定有可能都不存在了,须要从新构造队列和绑定。

主/备方式

当对可用性要求特别高时,不容许消息丢失,须要将队列、交换器、消息设置成持久化,若是一个节点崩溃了,在恢复以前,将没法转发消息,由于默认的群集架构不容许在集群其余节点建立队列,防止故障节点恢复后,历史消息丢失。

能够经过构建主/备机的独立RabbitMQ,也就是warren模式,解决这个问题。一个warren是指一对主/备独立服务器,并前置一套负载均衡器来处理故障转移。

主服务器和备用服务器之间没有协做,只有当主服务器崩溃时,备用服务器才会处理消息。能够保证,主节点故障后,经过备用节点从新建立队列、交换器继续服务,故障节点恢复后,能够继续消费主节点未消费的消息。

跨机房复制

在只有一个数据中心的时候,RabbitMQ集群对于提高消息通讯性能来讲是很棒的方案,但须要把消息从一个程序路由到另外一个城市的时候,就比较麻烦了,能够经过Shovel解决。

Shovel是RabbitMQ的一个插件,可使你可以定义RabbitMQ上的队列和另外一个RabbitMQ上的交换器之间的复制关系。说白了就是生产者和消费者离得比较远。

经过在机房1建立一个新的队列,用于接收网站发布的消息,而后让shovel消费这些消息并从新将消息经过WAN链接发布到机房2上的交换器。

这样对于用户来讲,只要发布到机房1的队列便可返回,减小了响应时间。机房1能够持续将消息发布到机房2上。

Shovel处理过程

经过上面的介绍能够看到,保证高可用须要作不少工做,能够根据业务对可用性的要求,选择不一样的架构方式。

下一篇重点介绍RabbitMQ管理界面和监控。

欢迎扫描下方二维码,关注个人我的微信公众号 ~

情情说
相关文章
相关标签/搜索