分区:
- rocketmq:messagequeue
- kafka:partition
rocketmq怎么保证消息有序
1.整个topic有序(全局有序),那就一个分区
- 缺点:性能较差,等于单线程处理
2.分区(messagequeue)有序
Sharding Key 是顺序消息中用来区分不同分区的关键字段,和普通消息的 Key 是完全不同的概念
- 生产者实现MessageQueueSelector接口
- 消费者实现MessageListenerOrderly接口
在默认的情况下:
- 消息发送会采取Round Robin轮询方式把消息发送到不同的queue(分区队列);
- 而消费消息的时候从多个queue上拉取消息,这种情况发送和消费是不能保证顺序
但是如果控制发送的顺序消息只依次发送到同一个queue中,消费的时候只从这个queue上依次拉取,
则就保证了顺序。
- 当发送和消费参与的queue只有一个,则是全局有序;
- 如果多个queue参与,则为分区有序,即相对每个queue,消息都是有序的。
消息的顺序消费,有三个关键点
- 消息顺序发送
- 消息顺序存储
- 消息顺序消费
第一点,消息顺序发送,多线程发送的消息无法保证有序性,因此,需要业务方在发送时,针对同一个业务编号(如同一笔订单)的消息需要保证在一个线程内顺序发送,在上一个消息发送成功后,在进行下一个消息的发送。
对应到mq中,消息发送方法就得使用同步发送,关键点在于单线程同步顺序发送消息
第二点,消息顺序存储,mq的topic下会存在多个queue,要保证消息的顺序存储,同一个业务编号的消息需要被发送到一个queue中。对应到mq中,需要使用MessageQueueSelector来选择要发送的queue,即对业务编号 sharding key 进行hash,然后根据队列数量对hash值取余,将消息发送到一个queue中。
关键点在于根据业务唯一编号Hash后选择同一消息队列(分区)
第三点,消息顺序消费,要保证消息顺序消费,同一个queue就只能被一个消费者所消费,因此对broker中消费队列加锁是无法避免的。同一时刻,一个消费队列只能被一个消费者消费,消费者内部,也只能有一个消费线程来消费该队列。即,同一时刻,一个消费队列只能被一个消费者中的一个线程消费。
关键点在于保证一个队列同一个时刻只能被一个消费者中一个线程消费