设计一个mq中间件,不得不考虑这些

mq简介

mq 全称message queue,也叫消息队列。通俗来讲也就是一个队列,这个队列用来存储消息的。生产者负责往队列里投递消息,消费者在队列中取消息。在分布式应用架构中,常用来做应用的解耦。

为什么用mq

  • 解耦。例:订单支付成功,需要给会员卡积分,优惠券核销,扣减库存,通知商家发货等。如果使用传统的方式,需要在订单支付成功后,需要与这么多系统发生交互。使用消息队列后,只需要各自需要订单支付成功的系统订阅支付成功的消息即可。
  • 异步,提高接口性能。订单支付成功后即可立即响应,如果耦合其它的调用,势必使接口调用时长提高。
  • 削峰。在请求数量暴增时,消息队列能适当“存储”一定的请求,峰值过后立即消化这部分积压请求。

使用mq带来的额外成本

  • 系统可用性降低。因为多引入了额外的消息中间件。依赖越多出问题的风险越大。
  • 系统复杂性提高:需要考虑多种场景,比如消息重复消费,消息丢失。
  • 需要更多的资源,比如机器以及人力维护: 消息队列一般集群部署,而且需要配套的运维和监控等

架构设计思路

最基本的架构

消息队列既然是一个队列,那么当然需要一个server来承载这些消息,于是我想到了如下的架构图。server负责维护全部queue的元数据信息。生产者投递消息到server,指定queue名称,消费者从queue中获取消息,这也是消息队列的核心思想。
设计一个mq中间件,不得不考虑这些_第1张图片
问题思考:由于使用者越来越多有一天,发现这个server有点扛不住压力了,响应非常缓慢,怎么办?

server横向扩展

无论你能想到其它优化方案,但是随着用户量越来越大,最终还是要考虑增加server 来分担压力。增加server需要考虑这样一个问题,所有的producer和consumer 都需要面临一次server地址的调整。这显然是不能接受的。于是我想到了如下的架构。再引入一个配置中心节点,配置中心负责维护所有server的元数据信息,所有的producer和consumer 也是直接和配置中心打交道,从配置中心获取到具体的server地址。
设计一个mq中间件,不得不考虑这些_第2张图片
问题思考:假如有一天我的流量暴增,导致我预先准备的server全都宕机了,此时服务都不可能,怎么办?

server的主备机制

这种情况是随时可能发生的,针对上面的架构我再做一些改进,于是我想到了如下架构。将我们的server分为master和slave。master节点可读可写,slave节点只可读。master宕机后,slave做为一个备份顶替master节点工作,必要时,还可对服务降级,比如master宕机后,防止slave也发生故障,可将slave设为只可读节点。
设计一个mq中间件,不得不考虑这些_第3张图片
问题思考:如果有一天我的 config-server 挂了怎么办 ?
如果config-server 挂了会导致整个服务瘫痪,因此conofig-server 也需要考虑集群部署模式。

不得不面临的挑战

数据完整性挑战

问题思考:server集群中如果出现某个节点宕机,或者整个集群宕机,如何保证我的消息不丢失?
这需要分两个步骤来讨论,生产者生产消息如何保证消息一定到达server,消费者如何一定能消费到消息。
生产消息 针对节点故障,导致消息丢失,在生产消息阶段,将消息同时发给不同的 master-server,多个master-server相互备份。但是这样一来,server的横向扩展就不能很好的分摊负载。一个消息需要多个server都确认后,才算发送成功,这样发送消息的效率也会大打折扣,但是这样做的好处是一旦写入成功,除非所有的节点都宕机才会导致消息丢失。
这让我又想到的另外一种实现方式。就是一条消息就写入一个master节点,但是在写入消息的时候同时需要备份到slave节点,而从master节点同步消息到salve节点的方式又可分为同步复制和异步复制。
什么是消息的同步复制和异步复制?

  • 同步复制,则表示,生产者发送消息到master节点,需要等slave节点将数据同步后,才告诉生产者消息发送成功。这种就性能稍低。
  • 异步复制,表示,生产者将消息发送到master节点后,立马响应消息发送成功。slave 复制 master的消息是异步完成的。 因此这种方式性能稍高。但是也存在一定的风险。例如,消息发送到master返回成功后,在消息复制过程中,master宕机了,这时还未被复制到slave 的消息就丢失了。

生产消息 针对整个集群不可用时,在集群重启或者修复后,依然要保证消息不丢失。这让我想到了消息的持久化。
什么是消息的持久化,持久化方式有哪些?

  • 消息持久化的意思是防止服务器重启或者完全宕机导致消息丢失采取的一种措施,例如将消息持久化到磁盘,当server重启时从磁盘加载数据,不至于消息丢失。
  • 同步持久化。意思是消息发送至server,收到消息后,要将消息持久化到磁盘后,才响应客户端消息发送成功。此方式数据安全性高,但是性能比较低。
  • 异步持久化。指的是消息到达服务端broker后,立马响应客户端消息发送成功。消息持久化的过程是异步完成的。这种方式性能比较高。但是存在风险。如果消息在持久化的过程中,此时broker突然宕机,就会导致消息丢失。

消费消息 在consumer消费消息时,必须向server发送ack确认。server如果没有收到确认,则会重新发送。

数据重复的挑战

消息重复,这是消息队列中一个常见的问题。producer投递的时候,需要有时候因为网络原因,会导致重复投放。consumer在消费消息时,未能及时响应,从而导致消息重复消费。一般来说,在消息消费端必须要做消息去重处理。
说道这里不得不说consumer 消费消息的两种方式

  • pull。consumer主动去server拉去消息。主动权在客户端,可控性好。但是拉取的时间间隔需要控制好,不能太短(空拉,浪费资源)也不能太长(消息不能及时消费),有一种比较方式就是长轮询,拉一次保活一定时间,比如15s。
  • push。server主动将消息推送到consumer端。效率高,实时性高。但是增加了服务端的负载。为提高push方式的效率,push同样可以做缓存,攒够一定的数量再一次性发送。

两种方式各有千秋,需看具体的场景做选择。

mq设计之锦上添花

使用场景的覆盖

通常的消息队列需要支持者有两种使用方式
第一,点对点模式,又叫队列模式,即producer往队列中放消息,consumer 从队列中取消息。如果存在多个消费者,只能有一个消费者抢到这个消息。
第二,点对多模式,又叫发布订阅模式,即producer不是向某个特定队列中放消息,而是往指定的主题,又叫topic中放消息。而需要这类消息的consumer,需要订阅此主题的消息,这样就自己就有一个专属的队列去接收这个topic下的全量消息,不至于消息被抢走。 经典的比喻场景就是微信公众号了,如果很多人订阅了一个公众号,那么每个人都会受到同样的消息推送。

跨平台多语言

跨平台,指的是能在不同的操作系统上部署与使用。多语言,支持不同语言的客户端进行使用。

mq常见的问题的处理

消息重复

要想保证消息不重复消费,必须在客户端做好去重,即保证消息消费的幂等性。在客户端可以根据业务主键进行去重,例如订单的订单id,具体的实现方式可用redis的分布式锁进行实现。也可以利用mysql 的主键唯一性处理。

消息积压

由于业务高峰,或者由于节点故障,消息不能被消费,导致消息量剧增,消息积压。如何快速的将波峰抹平。
如果是正常的业务消息积压,不去处理,到峰值过后波峰自然会被抹平,有时几小时,有时候可能一天还不能处理完。要想不影响新的业务消息,就需要另想他法了。
-这里建议临时扩充consumer的数量,提高consumer的消费能力。另一方面,可以申请临时的topic 通过临时程序将积压的消息转移到临时topic,提高更多的consumer数量。

消息可能无序

在实际使用消息队列时,必须要注意这个点,因为大多数情况下,我们是不保证消息有序的,即非常严格的 先进先出。 因为要保证这个顺序需要付出一定的性能代价,所以对于一般的消息,还要想好如何容纳这种错误。

市面上主流的mq对比总结

ActiveMQ

官网 http://activemq.apache.org/

优缺点

  • 优点。历史悠久,支持多种语言的客户端和协议,支持多种语言Java, .NET, C++ 等,基于JMS Provider的实现
  • 缺点:吞吐量不高,多队列的时候性能下降,存在消息丢失的情况,比较少大规模使用

Kafka

官网 http://kafka.apache.org/

优缺点

  • 优点。是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理大规模的网站中的所有动作流数据(网页浏览,搜索和其他用户的行动),副本集机制,实现数据冗余,保障数据尽量不丢失;支持多个生产者和消费者。一般在大数据领域使用较为广泛。
  • 缺点:不支持批量和广播消息,运维难度大,文档比较少, 需要掌握Scala

RabbitMQ

官网 http://www.rabbitmq.com/

优缺点

  • 优点。是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不错
  • 缺点:使用Erlang开发,阅读和对源码做二次开发难度大

RocketMQ

官网 http://rocketmq.apache.org/

优缺点

  • 优点。阿里开源的一款的消息中间件, 纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点, 性能强劲(零拷贝技术),支持海量堆积, 支持指定次数和时间间隔的失败消息重发,支持consumer端tag过滤、延迟消息等,在阿里内部进行大规模使用,适合在电商,互联网金融等领域使用
  • 缺点:部分实现不是按照标准JMS规范,有些系统要迁移或者引入队列需要修改代码

主流消息队列各有千秋,还是要结合具体的应用场景以及团队技术栈综合考虑。

总结

本文主要介绍使用mq的好处,以及使用mq给系统带来的额外成本。并且在架构层面上以层层递进的思路讲述了一个优秀的mq中间件应该要考虑到哪些地方。还讲述了mq中一些常见的问题处理方案。最后拿出市面上的一些主流的mq做了对比。
总之没有完美的架构,也没有完美的设计,有舍必有得。具体选择,还得看具体场景。

你可能感兴趣的:(理论交流)