mq

MQ 系列 消息顺序

刨析rocketMQ的底层实现

Posted by lichao modified on September 29, 2019

消息有序 指的是一类消息消费时,能按照发送的顺序来消费。例如:一个订单产生了三条消息分别是订单创建、订单付款、订单完成。消费时要按照这个顺序消费才有意义,但是同时订单之间是可以并行消费的。 顺序消息分为全局顺序消息与分区顺序消息,全局顺序是指某个 Topic 下的所有消息都要保证顺序;分区顺序消息只要保证每一组消息被顺序消费即可。

三种消息的类型介绍如下:

  • 普通消息:消息是无序的,任意发送到哪一个队列都可以。
  • 普通有序消息:同一类消息(例如某个用户的消息)总是发送到同一个队列,在异常情况下,也可以发送到其他队列。
  • 严格有序消息:消息必须被发送到同一个队列,即使在异常情况下,也不允许发送到其他队列。

支持情况

  • Kafka 支持消息顺序,但是一台代理宕机后,就会产生消息乱序。所有消息根据 sharding key 进行分区。同一个分区内的消息按照严格的 FIFO 顺序进行发布和消费。sharding key 是顺序消息中用来区分不同分区的关键字段,和普通消息的 key 是完全不同的概念。 适用于性能要求高,以 sharding key 作为分区字段,在同一个区块中严格的按照 FIFO 原则进行消息发布和消费的场景。
  • RocketMQ 支持普通/严格有序消息。在严格顺序消息场景下,一台 Broker 宕机后,发送消息会失败,但是不会乱序。

MySQL 的二进制日志分发需要严格的消息顺序

对于这三种类型的消息,RocketMQ 分别提供了对应的方法来发送消息,例如同步发送API(异步/批量/oneway也是类似): 有序消息发送

需要注意的是:

这些方法重载形式,本意是为了支持以上三种不同的消息类型。但是不按套路出牌,例如:对于一个用户的多条消息,在调用第一种send方法形式时,依然在对于同一个用户每次发送消息时,选择了不同的队列(MessageQueue),那么也没有人能阻止。这就忽略了 RocketMQ 团队设计这三个方法的意图。 RocketMQ 客户端的生产者重试机制,只会普通消息有作用。对于普通有序消息、严格有序消息是没有作用。

Kafka 可以保证同一个分区中的消息是有序的。如果生产者按照一定的顺序发送消息,那么这些消息也会顺序地写入分区,进而消费者也可以按照同样的顺序消费它们。对于某些应用 来说,顺序性非常重要,比如MySQL 的 binlog 传输,如果出现错误就会造成非常严重的后果。如果将 acks 参数配置为非零值,并且 max.io.flight.requests.per.connection 参数配置为大于 1 的值,那么就会出现错序的现象: 如果第一批次消息写入失败, 而第二批次消息写入成功,那么生产者会重试发送第一批次的消息, 此时如果第一批次的消息写入成功,那么这两个批次的消息就出现了错序。一般而言,在需要保证消息顺序的场合建议把参数。 max.io.flight.requests.per.connection 配置为 1 ,而不是把 acks 配置为0 , 不过这样也会影响整体的吞吐。