消息队列学习笔记

一、那些问题适合使用消息队列来解决

  常被使用的三种场景:异步处理、流量控制和服务解耦。还包括:

  • 作为发布/订阅系统实现一个微服务级系统间的观察者模式;
  • 连接流计算任务和数据
  • 用于将消息广播给大量的接收者。

  简单的说,我们单体应用里面需要用队列解决的问题,在分布式系统大多都可以用消息队列来解决。

  还有消息队列的自身的一些问题和局限性,包括:

  • 引入消息队列带来的延迟问题;
  • 增加了系统的复杂性;
  • 可能产生数据的不一致的问题。

二、如何选择消息队列

  消息队列产品的基本标准:首先必须是开源的产品,这个非常重要。意味着如果有BUG你可以修改,不用等到下一个版本发布。其次这个产品必须是近些年比较流行的并且有一定的社区活跃度的产品。流行的好处是如果是常用的场景遇到BUG的概率非常低,如果遇到了BUG基本上也能很快找到解决方案。最后,流行的产品周边的生态系统会有一个比较好的集成和兼容。

  还必须具备一下几个特性

  • 消息的可靠传递:确保不丢消息;
  • Cluster:支持集群,确保不会因为某个节点的宕机导致服务不可用,也不能丢消息;
  • 性能:具备足够好的性能,能满足大多数场景的性能需求。

  可供选择的消息队列产品:

  RabbitMQ: Erlang语言编写,支持AMQP协议。

  特点:Messaging that just works,"开箱即用的消息队列“。也就是说RabbitMQ是一个相当轻量级的消息队列,非常容易部署和使用。在生产者和队列之间增加了一个Exchange模块,可以根据配置的路由规则将生产者发出的消息分发到不用的队列中。

  问题:第一个,RabbitMQ对消息堆积的支持并不好。第二个,性能较差。大概每秒钟可以处理几万到十几万条消息。第三个,编程语言使用的是Erlang.比较小众并且学习曲线比较陡峭。

  RocketMQ:阿里巴巴在2012年开源的消息队列产品。后来捐赠给Apache。有这不错的性能,稳定性和可靠性。大多数情况可以做到毫秒级的响应。每秒钟大概能处理几十万条消息。作为国产消息队列与国外流程的同级产品稍逊,与周边生态的集成与兼容一般。

  Kafka: 使用Scala和Java语言开发,设计上大量使用了批量和异步的思想,使的Kafka能做到高性能,异步收发性能是三者中最好的,大约每秒处理几十万条。

  Kafka与周边生态系统的兼容性是最好的没有之一,尤其在大户数据和流计算领域,几乎所有的相关开源软件系统都会优先支持Kafka。

  Kafka这种异步批量的设计带来的问题是,它同步收发消息的响应时延比较高,不太适合在线业务的场景。

  ActiveMQ:最老牌的开源消息队列,功能和性能与现代的有明显的差距,仅限于兼容还在用的爷爷辈儿的系统。

  ZeroMQ:一个基于消息队列的多线程网络库

  Pulsar:新兴的开源队列产品,采用存储和计算分离的设计。

三、如何确保消息不会丢失

  用消息队列最尴尬的情况不是丢消息,而是消息丢了还不知道。

  检测消息丢失的方法

  利用消息队列的有序性来验证是否有消息丢失。基本原理:在Profucer端给每个发出的消息附加一个连续递增的序号,在Consumer端来检查这个序号。便能判断丢失的是那条消息。分布式系统要实现这个检测方法有几个问题需要注意,像kafka和RocketMQ不保证在Topic上的严格顺序,只保证分区上的消息是有序的,所以在分区的时候必须要指定分区,并且在每个分区单独检测消息序号的连续性。

  确保消息的可靠传递

  

  • 生产阶段:在这个阶段,从消息在Producer创建出来,经过网络传输发送到Broker端
  • 存储阶段:在这个阶段,消息在Broker端存储,如果是集群,消息会在这个阶段被复制到其他的副本上。
  • 消费阶段:在这个阶段,Consumer从Broker上拉去消息,经过网络传输发送到Consumer上。

  在生产阶段消息队列通过常用的请求确认机制,来保证消息的可靠传递,只要Producer收到了Broker的确认响应,就可以保证消息在生产阶段不会丢失。在编写消息发送的消息代码时,需要注意,正确返回值或者捕获异常,就可以保证这个阶段的消息不丢失。

  存储阶段,只要Broker在正常运行就不会出现丢消息的问题。如果Broker出现了故障还是可能丢消息的。如果对消息的可靠性要求比较高,可以通过配置Broker参数来避免因为宕机丢消息。 对于单节点的broker,配置参数在写入磁盘后再给Producer返回确认响应。多节点的Broker需要将Broker集群配置成至少将消息发送到两个以上节点,在发送确认响应。

  消费阶段采取和生产阶段类似的确认机制来保证消息的可靠传递。不要在收到消息后就立即发送消费确认,二十应该在执行完所有消费业务逻辑之后,再发送消费确认。 

 四、如何处理消费过程中的重复消息

   在MQTT协议中,给出了三种传递消息时候能够提供的服务质量标准,从低到高依次是:

  • At most once:至多一次。也就是说没什么消息可靠性保证,允许丢消息。
  • At least once:至少一次。也就是说不允许丢消息,大师允许少量重复消息。
  • Exactly once:恰好一次。不允许丢也不允许重复。这个是最高等级。

  这个质量服务标准不仅适用于MQTT,对所有的消息队列都是适用的。绝大部分消息队列提供的服务质量都是At least once,包括RocketMQ、RabbitMQ和Kafka.

  用幂等性来解决重复消息问题

  幂等本来是一个数学上的概念,它是这样定义的:如果一个函数f(x)满足:f(f(x)) = f(x),则f(x)满足幂等性。特点是其任意多次执行所产生的影响均与一次执行的影响相同

   At least once + 幂等消费 = Exactly once

  常用设计幂等操作的方法:

  • 利用数据库的唯一约束实现幂等
  • 为更新的数据设置前置条件
  • 记录并检查操作

五、消息积压了该如何处理

  消息积压的直接原因,一定是系统中的某个部分出现了性能问题,来不及处理上有发送的消息才会导致消息积压。对于绝大多数使用消息队列的业务来说,消息队列本身的处理能力要远大于业务系统的处理能力。所以,对于消息队列的性能优化,我们更关心的是,在消息的收发两端,我们的业务代码怎么和消息队列配合,达到一个最佳的性能

  发送端的性能优化:优先检查一下,是不是发消息之前的业务逻辑耗时太多导致的。

  消费端的性能优化

  一定要保证消费端的消费性能高于生产端的发送性能,这样的系统才能健康的持续运行。

  除了优化消费业务逻辑外,还可以通过水平扩容来提升性能。

  常见的错误解决消费慢的问题的方法: 在收到消息后不处理任何业务逻辑,把这个消息放到内存队列里面就返回。然后启动业务线程真正处理消息的业务逻辑。错误的原因为:在接受消息的节点发生宕机,在内存队列中还没有来及处理这些消息,消息就丢掉了。

  消息积压了怎么处理?

   导致积压突然增加,最粗粒度的原因只有两种:要么发送快了,要么消费慢了。

  大部分消息队列内置了监控功能,如果是单位时间发送的消息增多,唯一的方法就是扩容。如果短时间内不能进行扩容,没办法的办法是将系统降级,通过关闭一些不重要的业务,减少发送发发送的数据量。

  如果通过监控发现发送和消费的速度都没有变化,这时候就需要检查消费端,是不是消费失败的一条记录导致反复消费,或者消费错误等情况。

原文地址:https://www.cnblogs.com/OneSky-Mi/p/14357787.html