消息队列(message queue),简称MQ
1. 什么是消息队列
- 消息队列是在消息的传输过程中保存消息的容器,是应用间的通信方式。消息发送后可以立即返回,由消息系统保证消息的可靠传输,消息发布者只管把消息写到队列里面而不用考虑谁需要消息,而消息的发布者也不需要知道谁发布的消息,只管到消息队列里面取,这样生产和消费便可以做到分离
2. 为什么使用MQ
常见的用法是解耦、异步、削峰
- 解耦:避免模块之间直接调用,将所需的数据放在消息队列中,对于新增业务模块,只要对该类感兴趣,即可订阅该类消息,对原有系统和模块没有任何影响,降低了系统各个模块的耦合度,提高了系统的可扩展性
- 异步:把消息放到消息中间件中,不立即处理它,在之后需要的时候再慢慢处理
- 削峰:把高峰期的消息积压起来,在随后的时间内进行平滑的处理完成,而不至于让系统短时间内无法承载而导致崩溃
3. 说一说生产者与消费者模式
- 实际上是包含了两类线程,一种是生产者线程用于生产数据,另一种是消费者进程用于消费数据,为了解耦生产者与消费者的关系,通常会采用共享的数据区域,就像是一个仓库。生产者生产数据之后放到这里;消费者需要消费的时候从这里取数据,但这个共享数据区域中应该具备下面的线程间并发协作的功能
- 如果共享数据区已满的话,阻塞生产者继续生产数据放置入内
- 如果共享数据区为空的话,阻塞消费者继续消费数据
4. 消息队列如何保证顺序消费
- rabbitmq:可以个rabbitmq创建多个Queue,每个消费者固定一个queue的消息,生产者发送消息的时候,同一个订单号的消息发送到同一个queue中,由于同一个queue的消息一定是会保证有序的,那么同一个订单号的消息就只会被一个消费者顺序消费,从而保证了消息的顺序性
- Kafka:从生产者到消费者消费消息这一整个过程其实都是可以保证有序的,导致最终乱序是因为消费者端需要使用多线程并发处理消息来提高吞吐量,比如消费者消费到了消息以后,开启很多线程来处理消息,每个线程处理消息的快慢是不一致的,所以才会导致最终消息有可能不一致。解决办法就是让同一个订单号的消息只被同一个线程处理就可以了,我们可以在线程处理前加一个内存队列,每个线程只负责处理其中一个内存队列中的消息,同一个订单号中的消息发送到同一个内存队列中即可
- RocketMQ:每个Topic可以指定多个MessageQueue,当我们写消息的时候,会把消息均匀的分发到不同的MessageQueue中,比如同一个订单号的消息,增加binlog写入到MessageQueue1中,修改binlog写入到MessageQueue2中删除binlog写入到MessageQueue3中。但是当消费者有多个机器的时候,会组成一个Consumer Group,这个group里面个每一台机器都会负责消费一部分MessageQueue的消息,所以这时候我们无法保证这个消费是按照我们希望的顺序进行的。RocketMQ的消息乱序是由于同一个订单号的binlog进入到了不同的MessageQueue,进而导致一个订单的binlog被不同机器上的Consumer处理。要解决这一问题,我们只需要想办法让同一个订单的binlog进入到同一个MessageQueue中就可以了,因为同一个MessageQueue内的消息一定是有序的,一个MessageQueue中的消息只能交给一个Consumer来处理,所以Consumer消费的时候一定是有序的
5. RabbitMQ和kafka语言对比
- RabbitMQ是erlang语言开发,用在实时的对可靠性要求比较高的消息传递上;kafka是采用scala语言开发,主要用于处理活跃的流式数据,大数据量的数据处理上
6. RabbitMQ如何解决重复消费的问题
- 为了保证消息不被重复消费,首先要保证消息是唯一的,所以可以给每个消息带一个全局唯一的id,流程如下
- 消费者监听到消息后获取id,先去查询这个id是否命中
- 如果不存在,则正常消费消息,并把这个消息存入数据库或者redis中
- 如果存在则丢弃此消息