为什么使用消息中间件?
- 解耦
耦合性太强的话可以将消息写入消息队列,需要消息的系统自己从消息队列中订阅。 - 异步
将消息写入消息队列,非必要的业务逻辑可以以异步的方式运行,加快主体业务的速度。 - 削峰
并发量大的时候,所以的请求都直接对数据库,造成数据库异常,这个时候可以先把请求放入消息队列中,慢慢读取。
使用了消息队列有什么缺点?
-
系统可用性降低
如果消息队列挂掉了,系统肯定就不行了。
-
系统复杂性增加
加入消息队列需要考虑一些新的东西,比如一致性问题,如何保证消息不被重复消费,如何保证消息可靠性传输等。
常见的消息队列比较(如何选型)?
特性 | ActiveMQ | RabbitMQ | RocketMQ | kafka | ZeroMQ |
---|---|---|---|---|---|
开发语言 | java | erlang | java | scala | c |
单机吞吐量 | 万级 | 万级 | 10万级 | 10万级 | 最高 |
时效性 | ms级 | us级 | ms级 | ms级以内 | |
可用性 | 高(主从架构) | 高(主从架构) | 非常高(分布式架构) | 非常高(分布式架构) | |
功能特性 | 成熟的产品,在很多公司得到应用;有较多的文档;各种协议支持较好 | 基于erlang开发,所以并发能力很强,性能极其好,延时很低;管理界面较丰富 | MQ功能比较完备,扩展性佳 | 只支持主要的MQ功能,像一些消息查询,消息回溯等功能没有提供,毕竟是为大数据准备的,在大数据领域应用广。 | 内存中使用,不支持数据持久化,金融场景比较多见。 |
主要有两点:
- 中小型软件公司,建议选rabbitMQ,e语言天生具备高并发的特性,管理界面用起来十分方便。但是e语言的个性化定制不容易。不选rocketmq和kafka的原因是,数据量没那么大,kafka功能不完备,rocketmq是阿里的,也是不容易进行rocketmq的定制性开发。
- 大型软件公司,可以在rocketMQ和kafka之间二选一。有足够的资金搭建分布式环境,也具备足够大的数据量。如果有日志采集功能,首选kafka。
如何保证消息队列高可用?
以RocketMQ为例:他的集群就有多master模式、多master多slave异步复制模式、多master多slave同步双写模式。 感觉这里无非就是多台机器,和zookeeper什么类似。
如何保证消息不被重复消费(如何保证消息队列的幂等性?)
正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是不同的消息队列发出的确认消息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offet的概念,简单说一下,就是每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。
如果因为网络传输等故障,确认消息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他消费者。
如何解决?这个问题针对业务场景来答,分以下三种情况:
- 比如,你拿到这个消息做数据库的insert操作,那就容易了,给这个消息做一个唯一的主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
- 再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。
- 如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis.那消费者开始消费前,先去redis中查询有没有消费记录即可。
如何保证消费的可靠性传输?
我们在使用消息队列的过程中,应该做到消息不能多消费,也不能少消费。如果无法做到可靠性传输,可能给公司带来千万级别的财产损失。可靠性传输可以从这三个角度来分析。
- 生产者弄丢数据
- 消息队列弄丢数据
- 消费者弄丢数据
- 生产者丢数据
从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
transaction机制就是说,发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。然而会造成吞吐量下降。
-
消息队列丢数据
处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。 -
消费者丢数据
消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时rabbitMQ会立即将消息删除,这种情况下,如果消费者出现异常而未能处理消息,就会丢失该消息。