TDMQ-pulsar消息的顺序性

2022-05-06 13:20:59 浏览数 (2)

Pulsar中消息的顺序性和几个因素有关:用户自己的业务线程数、Producer 的路由模式(SinglePartition、RoundRobinPariion等、Topie是否分区、发送方式(同步、异步),是否开启批量发送、消息是否有Key。 每个Producer实例都有一个属于自已的发送队列,不管是同步发送还是异步发送,所有的消息都会先进入这个队列。同步发送是基于异步发送实现的----异步发送会返回一个CompletablePuture对象,同步发送只是在此基础上同步等特而已(通过CompletableFuture,get()实现)。因此,同步发送的消息也会先进入发送队列,不过每次入队后都会触发发送操作。

1、用户业务线程对顺序的影响

单线程向发送队列中放入消息肯定是有序的,如果有多个业务线程同时持有一个Producer对象,那么Thread-A放入消息时,可能Thread-B也在放入消息,即两个线程的消息会混在一个队列中,单个线程放入的消息可能并不是连续的,

比如Thread-A-消息1→Thread--B-消息1→Thread-A-消息2→Thread-A-消息3→Thread-B-消息2。

当然,如果有多个Producer对象,那么就有多个发送队列,最终到达Broker中某个Partition的消息也有可能不是连续有序的。Producer发送的消息和到达Broker的消息的顺序如图1所示。

图1图1

2、路由模式对消息顺序的影响

不考虑其他影响的前提下,如果单个Producer使用SinglePartition的路由方式,那么数据只会发送给一个Partition,到达Broker的数据肯定是有序的。如果使用RoundRobinPartition的路由方式(不设置消息的Key),那么消息就轮询地发送到每个Partition上,没有顺序可言。

3、Topic分区对顺序的影响

如果Topic只有一个Partition,那么就和SinglePartition的路由方式一样了,Producer只能把消息发送到一个Partition,客户端发送队列的顺序会完整地复制到Broker上。有一种例外的情况一Producer关闭了批量发送,但是使用了异步发送。我们可以看这样一个场景,Producer异步发送时有多个消息入队,不会等待前一条消息的响应结果就继续发送下一条消息,因此消息不断地被发送到Broker。。在Broker端持久化时,由于关闭了批量发送,每条消息会被存入一个独立的Ey中,这批消息中间可能有部分消息持久化失败,但其他消息还是会正常持久化,那么持久化失败的这条消息会被异步告知Producer发送失败了,此时Broker中的消息顺序就和发送队列中的消息顺序不一样了。

发送队列中的消息顺序为1一2一3→4,假设消息3持久化失败了,其他消息都持久化成功,客户端得知后再重试,则Broker的消息顺序为1→2→4→3。

4、发送方式对顺序的影响

如果是同步发送,那么只有得知上一条消息成功/失败后,客户端才会继续发送下一条消息。因此,如果发送到单分区,那么Partition中的消息顺序和发送队列中的消息顺序是完全一样的。

如果是异步发送,那么出现顺序不一致的情况就和上面描述的场景一样。另外,网络抖动等其他因素也有可能导致消息乱序。但异步发送是并行发送,通过回调通知结果,因此性能肯定比同步发送要好。异步发送会返回一个CompletableFuture对象,消息发送成功或者失败都会返回对应的回调对象。

5、是否开启批量发送对顺序的影响

如果开启批量发送,那么多条消息会作为一批消息发送到Broker。由于开启了批量发送,所以这一批消息会保存在一个Ety中,这样就不会出现部分消息发送成功、部分消息发送失败的情况。此时整个Ety被同一个消费者消费,不会出现部分消息推送给其他消费者的情况。但有另外一个问题,如果整批消息都发送失败,那么批与批之间的顺序可能就乱了。

6、消息是否有Key对顺序的影响

消息是否有Key是一个很重要的前提,假设我们使用默认的RoundRobinPartition路由方式,在消息没有设置Key的情况下,Producer会以轮询的方式发送消息。如果消息中存在Key,则相同Key的消息都会被投递给同一个Partition,同一个Partition中的消息是有序的。

上面提到这些因素的组合非常多,我们不能一一列举所有场景的组合情况,但由于消息发送的原理是固定的,读者可以根据自己的场景推导出不同场景下消息的顺序性。

0 人点赞