消息队列

消息队列

消息队列堆积?

MQ消息堆积,一般都是由消费侧能力不足导致的。可能的原因如下:

  1. 生产侧流量暴涨,消费侧消费速度不变
  2. 生产侧流量不变,消费侧消费速度下降

针对原因1,可以通过横向扩容实例的方式来提升消费速度,解决积压问题;

针对原因2,横向扩容只能临时缓存消费积压问题,根本原因在于消费服务内部出现问题,可能是自身服务性能、下游接口耗时增加等导致整体吞吐量降低

EventBus生产侧监控:

img

消费侧监控:

img

由两图可以看出生产侧QPS与消费侧QPS几乎持平,理论上不存在积压才对……,why?遇到奇怪的、想不通的问题找Oncall

联系NSQ Oncall得知:消费者侧只有一个client与NSQ相连接

链路:https://grafana-us.byted.org/d/000006115/nsq-channel-aliyun-mva?orgId=1&var-topic=live_room_event_topic&var-channel=tikcast_room_live_notice&var-cluster=i18n_maliva_ies_normal1_mva&var-client_cluster=ies_normal1&from=1629302400000&to=1629320399000

img

img

但查看FaaS函数,函数实例数明明有10个呢?为什么只有一个实例与NSQ相连?

联系EventBus Oncall得知:针对MQ触发器,FaaS任务是一个消息转发服务,先消费NSQ的消息,再将消息发送给FaaS函数,链路:MQ -> FaaS任务 -> FaaS函数实例,在配置MQ触发器时,默认消费者数量(这里是指FaaS任务消费NSQ的实例数)为1,也即表示者只有一个实例与NSQ相连,破案了。

img

解决问题

将MQ触发器数量调整为20,效果如下(11:16左右调整的),消费积压状况之后未出现

img

NSQ侧消费实例数亦恢复正常

img

反思

NSQ监控需要去NSQ平台查看才可以,EventBus平台生产监控不准确,查看方法:

img

1.出现rpc超时问题,经过排查

消息队列是怎么选型的?

http://www.mstacks.com/133/1401.html#content1401

消息队列的特点

先说一下消息队列常见的使用场景吧,其实场景有很多,但是比较核心的有 3 个:解耦异步削峰

解耦

image-20210830184036940

异步

image-20210830184011396

削峰

image-20210830184138515

消息队列有什么优缺点

优点上面已经说了,就是在特殊场景下有其对应的好处解耦异步削峰

缺点有以下几个:

  • 系统可用性降低

系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了。高可用:kafka的高可用。Leader ->follower

  • 系统复杂度提高

硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?

  • 一致性问题

A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。

所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了 10 倍。但是关键时刻,用,还是得用的。

Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?

特性 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级,比 RocketMQ、Kafka 低一个数量级 同 ActiveMQ 10 万级,支撑高吞吐 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic 数量对吞吐量的影响 topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性 ms 级 微秒级,这是 RabbitMQ 的一大特点,延迟最低 ms 级 延迟在 ms 级以内
可用性 高,基于主从架构实现高可用 同 ActiveMQ 非常高,分布式架构 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性 有较低的概率丢失数据 基本不丢 经过参数优化配置,可以做到 0 丢失 同 RocketMQ
功能支持 MQ 领域的功能极其完备 基于 erlang 开发,并发能力很强,性能极好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用

衡量一款消息队列是否符合需求需要从多个维度考察,但是首要的还是考察功能维度,这个直接决定了你能否最大程度上实现开箱即用,进而缩短项目周期、降低成本等。技术是为了业务服务,技术选型要优先满足业务述求。我们来看一下一些消息队列常见功能诉求有哪些。

性能

功能维度是消息中间件选型中的一个重要的参考维度,但这并不是唯一的维度。有时候性能比功能还要重要,况且性能和功能很多时候是相悖的,鱼和熊掌不可兼得,比如Kafka开启同步刷盘,其性能就会降低。

消息中间件的吞吐量始终会受到硬件层面的限制。就以网卡带宽为例,如果单机单网卡的带宽为 1Gbps,如果要达到百万级的吞吐,那么消息体大小不得超过 (1Gb/8)/100W,即约等于 134B,换句话说如果消息体大小超过 134B,那么就不可能达到百万级别的吞吐。这种计算方式同样可以适用于内存和磁盘。

前期公司主要的MQ服务是Nsq 和 Kafka,但是仍然有一些需求没有被覆盖到,引入RocketMQ主要是针对线上服务,提供可靠存储的消息中间件,并提供Nsq和Kafka未能提供的功能,并解决Nsq和Kafka在使用过程中的问题。

  • 功能丰富:支持延时消息,死信队列,消息重试,消息回溯、事务消息等高级特性,另外消息体支持消息头,这个是非常有用的,可以直接支持实现消息链路追踪,不然就需要把追踪信息写到 message 的 body 里。
  • 稳定性:这是一个经过阿里巴巴多年双11验证过的、可以支持亿级并发的开源消息队列,是值得信任的。和 Kafka 一样是先写 PageCache ,再落盘,并且数据有多副本。
  • 性能:存储模型是所有的 Topic 都写到同一个 Commitlog 里,是一个append only 操作,在海量 Topic 下也能将磁盘的性能发挥到极致,并且保持稳定的写入时延。采用一主两从的结构,单机 qps 可以达到 14w , latency 保持在 2ms 以内。对比NSQ 和 Kafka , Kafka 的吞吐非常高,但是在多 Topic 下, Kafka 的 PCT99 毛刺会非常多,而且平均值非常长,不适合在线业务场景。另外 NSQ 的消息首先经过 Golang 的 channel ,这是非常消耗 CPU 的,在单机 56w 的时候 CPU 利用率达到 5060% ,高负载下的写延迟不稳定。

特点

image-20210830192114779

1、消费者发送的Message会在Broker中的Queue队列中记录。 2、一个Topic的数据可能会存在多个Broker中。 3、一个Broker存在多个Queue。 4、单个的Queue也可能存储多个Topic的消息。

Queue不是真正存储Message的地方,真正存储Message的地方是在CommitLog

如图(盗图)

image-20210830192226825

commitLog文件存放实际的消息数据,每个commitLog上限是1G,满了之后会自动新建一个commitLog文件保存数据。

一个broker通常是一个服务器节点,broker分为master和slave,master和slave存储的数据一样,slave从master同步数据。

1)nameserver与每个集群成员保持心跳,保存着Topic-Broker路由信息,同一个topic的队列会分布在不同的服务器上。

2)发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。发送消息指定topic、tags、keys,无法指定投递到哪个队列(没有意义,集群消费和广播消费跟消息存放在哪个队列没有关系)。

tags选填,类似于 Gmail 为每封邮件设置的标签,方便服务器过滤使用。目前只支 持每个消息设置一个 tag,所以也可以类比为 Notify 的 MessageType 概念。

keys选填,代表这条消息的业务关键词,服务器会根据 keys 创建哈希索引,设置后, 可以在 Console 系统根据 Topic、Keys 来查询消息,由于是哈希索引,请尽可能 保证 key 唯一,例如订单号,商品 Id 等。

PUSH VS PULL

pull和push指的是:由 consumer 从 broker 那里 pull 数据,还是由 broker 将数据 push 到 consumer。(kafka采用的是pull)

push-based系统是由broker来控制数据传输速率, 所以 push-based 系统很难处理不同的 consumer;让 broker 控制数据传输速率主要是为了让 consumer 能够以可能的最大速率消费,有更好的时效性;不幸的是,这可能会导致在 push-based 的系统中,当消费速率低于生产速率时,consumer 往往会不堪重负。

pull-based系统有一个很好的特性, 那就是当 consumer 速率落后于 producer 时,可以在适当的时间赶上来。

简单的 pull-based 系统的不足之处在于:如果 broker 中没有数据,consumer 可能会在一个紧密的循环中进行轮询,实际上 busy-waiting 直到数据到来。为了避免 busy-waiting,我们在 pull 请求中加入参数,使得 consumer 在一个“long pull”中阻塞等待,直到数据到来(还可以选择等待给定字节长度的数据来确保传输长度)。

详细对比

RocketMQ Kafka
消息投递方式 pull + PUSH pull
延时队列 支持 不支持
死信队列 支持 不支持
消息重试 支持 不支持
可靠消息 不丢消息
吞吐 十万