RocketMQ常见问题——顺序消费原理以及实现

        RocketMQ提供局部顺序一致性的机制,Broker为每个Topic每个QueueID构建消息队列(ConsumerQueue),实现了单个队列中的消息严格有序。按照业务划分消息队列,然后将需要顺序消费的消息发往同一队列,消费者根据消息队列拉取消息进行消费,即可做到顺序消费。
顺序消费原理
1.生产者Producer端:生产端实现有序性,依赖于MessageQueue选择能力,RocketMQ提供MessageQueueSelector接口用于消息队列选取。
2.服务端Broker:Broker为每个Topic每个QueueID构建ConsumerQueue。
3.消费者Consumer端:消费端提供监听器MessageListenerOrderly接口,消费者需要依据业务要求实现这个接口即可。
以上是RocketMQ提供的机制,下面我们一起看一下源码。
源码常看
1.生产端Producer端,生产端提供MessageQueueSelector接口,同时提供了根据hashCode,MachineRoom,Randoom三种选择方式;提供了selectOneMessageQueue实现选择MessageQueue的方法,我们需要根据自己的业务需求实现。

public interface MessageQueueSelector {
    MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}

1.1SelectMessageQueueByHash

public class SelectMessageQueueByHash implements MessageQueueSelector {

    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        int value = arg.hashCode();
        if (value < 0) {
            value = Math.abs(value);
        }

        value = value % mqs.size();
        return mqs.get(value);
    }
}

1.2SelectMessageQueueByMachineRoom

public class SelectMessageQueueByMachineRoom implements MessageQueueSelector {
    private Set<String> consumeridcs;


    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        return null;
    }


    public Set<String> getConsumeridcs() {
        return consumeridcs;
    }


    public void setConsumeridcs(Set<String> consumeridcs) {
        this.consumeridcs = consumeridcs;
    }
}

1.3SelectMessageQueueByRandoom

public class SelectMessageQueueByRandoom implements MessageQueueSelector {
    private Random random = new Random(System.currentTimeMillis());


    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        int value = random.nextInt();
        if (value < 0) {
            value = Math.abs(value);
        }

        value = value % mqs.size();
        return mqs.get(value);
    }
}

1.4生产者发送消息提供的默认实现DefaultMQProducerImpl中有多个根据selector发送消息的实现。我们根据要求调用即可。

public class DefaultMQProducerImpl implements MQProducerInner {
   private SendResult sendKernelImpl(final Message msg, //
                                      final MessageQueue mq, //
                                      final CommunicationMode communicationMode, //
                                      final SendCallback sendCallback, //
                                      final TopicPublishInfo topicPublishInfo, //
                                      final long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
  ………省略实现代码…………
  }

}

2.消费端Consumer,消费端提供MessageListenerOrderly接口,具体的业务逻辑需要我们自己实现

public interface MessageListenerOrderly extends MessageListener {
    /**
     * It is not recommend to throw exception,rather than returning ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT if consumption failure
     *
     * @param msgs
     *         msgs.size() >= 1<br>
     *         DefaultMQPushConsumer.consumeMessageBatchMaxSize=1锛寉ou can modify here
     * @param context
     *
     * @return
     */
    ConsumeOrderlyStatus consumeMessage(final List<MessageExt> msgs,
                                        final ConsumeOrderlyContext context);
}

总结:
RocketMQ的顺序消费是相对性的,发送消息在分布式环境下无法保证broker接收消息的顺序;broker无法根据收到的消息顺序,构建正确的MessageQueue顺序;消费者在分布式环境下也无法保证获取到消息的顺序性,业务实现由于业务复杂度不一样,也不能保证获取到的消息能按顺序处理完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值