我:这里不太熟悉诶。让我想一下:假设A和B通过消息队列通信,A发了2条消息m1和m2,这两条消息有依赖关系,必须先消费m1再消费m2。结果m2先到达,m1后到达。那么B可能先接收到m2,后接收到m1,这个时候应该怎么办?

首先,B接收到消息的顺序跟A发出的顺序以及队列中的顺序没有关系。消息到达队列的先后顺序没法保证,队列将消息投递出去以后接收消息的顺序也没法保证。所以,消费者B必须保证业务的正确性,无论生产者怎么发消息,队列怎么投递消息,消费者都必须意识到:接收到的消息是无序的,必须做好处理。其次,消息顺序不重要,重要的是最终一致性。

以签到送积分场景为例,假设签到和送积分是分开的。签到的处理方式是向签到表中插入一条记录,送积分的逻辑是判断积分所对应的签到记录是否存在,存在则送积分。消息队列中有两条消息,一条是签到消息,另一条是送积分消息,这条积分消息带着签到记录的id。那么,送积分就要依赖是否有签到记录。按照上面思路,应该将消息先保存在本地的一张表中,然后定时扫描这张表,签到送积分的逻辑是可以正常进行的。

面试官:但是消费者还是无法按顺序接收消息?

我:诶?等等。我回去学一下。

过了几天。

我:喂,大佬还在么?

通常MQ可以保证消息按顺序分发给消费者,但是一个队列有多个消费者,消息被多线程并发消费,从而可能导致后发送的消息先消费。有的时候消息按照顺序处理很重要,我们如何保证消息的顺序呢?

ActiveMQ处理方案

1、利用activemq的高级特性:exclusive consumer。

在ActiveMQ4.x中,可以采用Exclusive Consumer,让broker一次发送消息给一个消费者,避免多个消费者并发消费,这样就保证了顺序。

独占消息就是在有多个消费者同时消费一个queue时,可以保证只有一个消费者可以消费。这样虽然保证了消息的顺序问题,不过也带来了一个问题,就是这个queue的所有消息将只会在这一个主消费者上消费,其他消费者将闲置。

在实际业务中,我们更多是这样的场景,比如一个订单会发出一组顺序消息,我们只要求这一组消息是顺序消费的,而订单与订单之间是可以并行消费的,有没有办法做到呢?答案是可以,用activemq的另一个高级特性messageGroup。

2、ActiveMQ的高级特性:Message Groups

Message Groups是一种负载均衡机制。在一个消息被分发到consumer之前,broker首先检查消息JMSXGroupID属性。如果存在,那么broker会检查是否有某个consumer拥有这个message group。如果没有,那么broker会选择一个consumer,并将它关联到这个message group。此后,这个consumer会接收这个message group的所有消息,直到Consumer被关闭或Message group被关闭。

同一个queue中,拥有相同JMSXGroupID的消息将发到同一个消费者,解决顺序问题,不同分组的消息又能被其他消费者并行消费,解决负载均衡问题。

3. 其他MQ怎么保证消息按发送的顺序消费呢?

核心思想就是让需要按顺序消费的消息只能被同一个consumer消费。具体怎么做到呢?

1、消息要有顺序,首先要保证producer发送消息有顺序,例如发送ms1,msg2, msg3。producer必须等待前面的消息发送成功,再发送后面的消息。

2、默认情况下,当producer往broker发送消息时,消息会存储在topic下的不同队列中。而一个队列只会被一个consumer消费,消息会被均衡负载到不同的队列,也就是会被多个消费者并行消费,顺序就无法保证了。现在,把需要顺序消费的消息发送到同一台broker下的同一个队列,这样就可以保证顺序了。

总之,在分布式系统中,要想消息按顺序消费要想办法让有顺序的消息被同一消费者消费,而不是并发消费。在消费者消费成功后,接着才会消费下一个消息,这样就可以保证顺序。

面试官:(已离线)

我:啥时候发offer啊?

希望这篇文章能对你有帮助!如果对互联网、前/后/客户端、架构/分布式/高可用/高并发/高实时、电商、Redis、MySQL、Zookeeper、Spring、Android、浏览器插件、Java、C/C++、Linux、个性化推荐、社区发现、机器学习、数据挖掘等感兴趣,欢迎关注评论领取更多面试资料。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐