消息队列的exclusive consumer功能是如何保证消息有序和防止脑裂的

通常来讲,消息队列都会保证queue当中的消息的顺序。然而若是有多个consumer同时消费同一个queue,那么这时就不能保证的消息的顺序性。java

有时候,消息的顺序是很是重要的,为了能顺序的消费消息,咱们只能启动一个consumer来消费这个queue。可是这样作的问题就是,若是这个consumer宕调了话,消息就不能获得处理了,consumer的可用性不能获得保证。markdown

那么如何提升consumer的可用性那,一般的作法是启动多个consumer,让其中一个consumer成为master,其余的为slave,成为master的consumer才去消费queue中的消息(一般的作法是使用zookeeper进行选主)。code

可是这仍然没有解决所有问题。假设master在要消费一条消息以前,发生了一次很长的gc(假设咱们是一个java的程序,固然也有其余的致使这个问题的场景),zookeeper认为master挂了,从新选主,一个slave被提高为master,而且开始消费这个queue的消息,这时原来的master从gc中恢复,在必定的时间范围内,原master尚未收到zookeeper通知它已经不是主了,这时它会继续消费queue中的消息。也就是说,在这段时刻,咱们有2个认为本身是master的consumer。这种现象是分布系统中所说的脑裂。Zookeeper能够用来选主,zookeeper会保证只选出一个主,可是zookeeper并不能保证在某一时间系统中只有一个主,也就是说用了zookeeper并不能保证脑裂不会发生。Zookeeper不是用来防止脑裂的。rabbitmq

那么如何解决这问题那?不少mq都有exclusive consumer的概念,咱们能够用它来解决这个问题。首先咱们来讲一下,什么是exclusive consumer。队列

拿activemq来讲,咱们在建立queue时,能够指定这个queue是exclusive,好比消息队列

queue = new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true");

若是一个queue设置为exclusive,broker会调选一个consumer,而且将全部的消息都发给这个consumer。若是这个consumer挂了,broker会自动挑选另一个consumer。it

对于rabbitmq来讲,有2个exclusive参数能够设置:第一个是declare一个queue时能够指定exclusive=ture(也即queue_declare方法),那么这个queue只能被当前链接访问,当链接断开时queue会被删除。也能够这么理解exclusive queue是私有queue,non-exclusive queue是共享queue。第二个是consumer一个queue时能够指定exclusive=ture(也即basic_consume),那么这个queue只能被这个consumer访问。ast

脑裂问题咱们是不能避免的,能够避免的时在发生脑裂时,2个master能同时去消费queue中的数据。因此咱们应该用zookeeper来选主,让主去消费队列,而且队列要设置成exclusive。这样咱们就保证队列中的消息是被顺序消费的。class