MQ一篇全

205 阅读9分钟

RocketMQ和kafka的对比

  • 性能:kafka百万级别,rocketmq十万级?
  • 单机队列数:rocketmq,单机最高5万个队列,kafka单机超过64个就会load变高,是的响应时间变长。单机可以创建更多的Topic,Consumer的规模也是和队列数成正比
  • 消息实时性:rocketmq使用长轮询,同push方式实时性一致,kafka实时性取决于轮询间隔时间
  • 顺序消息:如果broker宕机则,rocketmq会发送失败但不会乱序?,kafka会乱序
  • 定时消息:rocketmq支持
  • 重试:rocketmq支持
  • 消息查询:rocketmq可以通过msgId查询消息
  • 消息回溯:rocketmq可以精确到毫秒根据时间来回溯,kafka理论上可以根据offset来回溯消息
  • 开发语言的友好性:rocketmq采用JAVA编写
  • 消息堆积能力:理论上kafka要比rocketmq强,但rocketmq也有单机亿级别的堆积,基本够用

kafka吞吐量

零拷贝——基于sendfile实现零拷贝

  1. sendfile系统调用,文件数据被copy至内核缓冲区
  2. 文件数据从内核缓冲区到用户缓冲区
  3. 再从用户缓冲区copy至内核中socket相关的缓冲区
  4. 最后再socket相关的缓冲区copy到协议引擎

在2.1版本减少了第二步,可直接从内核缓冲区到socket缓冲区

在2.4版本之后,文件描述符改变,再次减少了一次copy操作

通过零拷贝技术,就不需要把OS里的数据拷贝到应用缓存,再从应用缓存拷贝到Socket缓存,这两次拷贝都省略了,所以叫做零拷贝。

对Socket缓存仅仅就是拷贝数据的描述符过去,然后数据直接从OS Cache中发送到网卡上去

磁盘顺序写

页缓存技术

批量压缩

将多个消息一起压缩而不是单个消息压缩

rocketmq核心模块

  • broker:接收生产者发来的消息并存储,消费者从这里取得消息
  • client:提供发送、接收消息的客户端API
  • namesrv:类似于zk,这里保存着TopicName,队列等运行时元信息
  • remoting:分布式处理方式,基于Netty4的client/server + fastjson序列化 + 自定义二进制协议。
  • store:存储消息,索引引擎。
  • filtersrv:消息过滤器Server
  • tools:命令行工具

rocketmq核心架构组成

  • NameServer(主要负责对于源数据的处理,包括了对于Topic和路由信息的管理)

  • 集群化部署,是一个功能齐全的服务器,类似于dubbo中的zk,轻量、节点之间相互独立。

  • 30秒心跳机制和120秒的故障检查机制,平时主要在维持心跳和提供Topic-Broker的关系数据

  • 每个Broker在启动的时候会到每一台NameServer注册,同时每隔30秒会向NameServer发送心跳

  • 如果宕机,每个Broker都有Slave节点进行备份,消费者可以继续从Slave上拉去信息。

  • Producer在发送消息前会根据Topic到NameServer获取Brooker的路由信息,

  • Consumer也会定时获取Topic的路由信息。

  • Broker(消息中转角色,负责存储消息,转发消息)

  • 每个节点与所有的NameSrv保持长连接及心跳,并定时将Topic注册到NameSrv,底层通信和链接基于Netty实现

  • 负责消息的存储,以Topic为维度支持轻量级队列,具有上亿的消息堆积能力,同时保证消息的有序性。

  • Producer(消息生产者,负责生产消息)

  • 三种发送方式:同步、异步、单向

  • **同步发送:**发送方发出消息后,收到接收方响应后才发下一个数据包,一般用于通知重要消息。

  • **异步发送:**发送方发出消息后,不等接收方回响应

  • **单向发送:**单向发送是指只负责发送,不等待响应

  • Consumer

  • 支持集群消费和广播消费,提供实时的消息订阅机制

mq集群模式

  • 多master模式

  • 优点:配置简单,单个master宕机或重启维护对应用无影响,性能最高。

  • 缺点:单台机器宕机时,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性收到影响

  • 多master多salve——异步复制(每个master配置一个slave,HA采用异步复制方式,主备毫秒级延迟)

  • 优点:即使磁盘损坏,消息丢失非常少,消息的实时性不受影响,可用性高,性能高

  • 缺点:master宕机,磁盘损坏的情况下回丢失少量消息。

  • 多master多salve——同步双写(每个master配置一个slave,HA采用异步同步双写,只有主备都成功才返回成功)

  • 优点:数据与服务无单点故障,master宕机,消息无延迟,服务可用性和一致性都非常高

  • 缺点:性能比异步复制模式略低(大约10%左右)

  • Dledger 多副本模式

  • 3个Broker组成,一个组由一个master,两个slave组成,通过Raft协议来进行选举。

  • 优点:master挂后会自动选举新的master。

Raft协议

term选举周期和分裂选举

  • Leader 
  • Follower 
  • Candidate 候选人

刷盘

先写道commitlog(映射区),再到磁盘

  • 同步刷盘
  • 异步刷盘

消息重复

mq一般都要保证至少一次,在一些特殊情况难免避免消息重复。

由于网络原因闪断,ACK返回失败等等故障,确认消息没有传送到消息队列,导致消息队列不知道自己已经消费过消息。

  • 幂等
  • 业务去重

顺序消息

  1. 生产者顺序发送,不要使用多线程
  2. 顺序写入broker,使用hash取模队列传同一个hashkey,就可以进去同一个queue队列,由先进先出的特性
  3. 消费者消费的时候只有一个线程,不然也有可能因为速率不同导致落库有先后

事务消息

  1. 生产者发送半消息(暂时不能投递消费者的消息),需要等生产者二次确认才可投递。
  2. MQ服务端给生产者发送ack,告诉生产者半消息成功收到。
  3. 生产者执行本地数据库事务。
  4. 执行成功后就告诉broker进行commit。
  5. 未收到第4步的消息,回查事务状态。
  6. 发送方收到消息回查后,检查对应消息本地事务的最终结果。
  7. 发送方根据检查本地事务的最终状态再次二次确认,发送commit或者rollback。

延迟消息

  • 可以使用rocketmq的延迟队列来实现,开源版只支持指定精度的延时。
  • 对于broker收到延时消息,会进入专门的topic中,有18条专门队列和延迟等级一一对应。
  • 定时任务根据上次拉取的偏移量不断从队列中取出消息

消息堆积

  • 怎么发现消息堆积

  • 消息发送速率,远大于消息消费速率

  • 堆积场景

  • 消费端阻塞

  • jstack -l pid 去查ConsumeMessageThread的线程状态,如果是blocked那就阻塞了,如果是runnable状态一般来说是正常的

  • 消息堆积在内存Buffer

  • 扩容或者丢弃部分消息

  • 消息堆积在磁盘

  • 当不能命中cache时,不可避免的会访问磁盘,会产生大量读IO

  • 增加Topic的队列数和消费者数量(一个消费者一个队列)

  • 扩大队列容量,提高堆积上限

  • 集群故障

  • 有brocker挂了

保证消息不丢失

如何知道消息丢失?哪些环节可能丢失?如何确保消息丢失?

消息可能丢失的阶段有:

  • 消息产生阶段:

  • 从消息生产出来提交给MQ的过程中,只要能正常收到Broker的ack就表示发送成功。

  • 消息存储阶段:

  • 这个一般交给mq中间件来保证,比如Broker会有主从,有同步复制和异步复制两种

  • 如果主挂了,消费者也可以自动从备机中获取消息

  • 追求强一致性的话,可以等同步两个节点后再返回ack

  • 消息消费阶段:

  • 消费端的话等业务逻辑执行完后,再返回成功,失败的话也会有mq重试机制保证消息不丢失

上述方案时从mq架构设计上描述在各阶段都有对应的防丢失措施,

在某些必要情况下可以添加业务消息检测,比如给每个发出的消息里都指定一个全局唯一id或者连续递增的id生成落库,拦截器检测消息版本号更新消费状态。

或者有些业务场景可以添加中间态来检测,比如在完成和奖品发放成功中有一个发放中的中间态,可以通过数据库来检查是否有漏发情况,可以定时任务补发。

存储结构

RocketMQ文件主要包括Commitlog文件,ConsumeQueue文件,Index文件。

Commitlog 消息存储的物理文件

RocketMQ将所有主题的消息存储在同一个文件中,确保消息发送时按顺序写文件,尽最大能力确保消息发送的高可用性和高吞吐量。

ConsumeQueue 消息消费的逻辑队列(异步写入)

消息中间件一般都是基于主题的订阅与发布模式,消息消费时必须按照主题进行筛选消息,显然从commitlog中按照同topic去筛选消息效率很低,为了提高根据主题检索消息的效率,RocketMQ引入了ConsumeQueue文件。这里包含消息在CommitLog中的物理偏移量。(commitLogOffset、msgSize、tagsCode)

indexFile 消息索引文件(异步写入)

提供了对CommitLog进行数据检索,提供了一种通过key或者时间区间来查找CommitLog中的消息的方法

Kafka的分区副本和RocketMQ的队列的区别

rockemq的队列

rocketmq的每个Topic都会有若干个队列,分布于集群中各个broker上,每个队列只能由一个消费者进行订阅消费,但一个向消费者可以订阅多个队列。

Kafka

分区机制使得kafka具备了水平扩展的能力,在分区上,kafka还可以设置分区副本,大大提高了消息的可靠性。

和rocketmq一样,一个分区只能由一个消费者进行订阅消费,一个消费者可以消费多个分区。

可以通过调整主题分区数量来提高消息的吞吐量,还可以设置分区的副本因子。

参考:

rocketmq和kafka的对比

rocketmq和kafka的对比-吞吐量