消息队列RabbitMQ、RocketMQ、Kafka

消息队列

RabbitMQ、RocketMQ、Kafka 区别?

RabbitMQ的延时最低,微秒级别,单机吞吐量不好,高可用为主从,请求可以请求从机,但是从机上并没有消息数据,需要根据从主机同步过来的queue配置信息去主机上拉取过来返回给请求端。

RocketMQ延时毫秒级别,吞吐量高,topic几百到几千时,吞吐量也不会下降太多。分布式架构可用性高。

Kafka延时毫秒以内级别,吞吐量高,topic几十到几百时,吞吐量下降很多。分布式架构,一个数据多份副本集。

为什么使用消息队列?

解耦:生产者没必要去关心到底有哪些消费者需要消费,他可以将消息写入到mq中,有需要的对接系统自己来MQ中取就可以了

异步:如果一个功能处理起来耗时很长,这样会给用户很不好的体验,所以可以将需要做的处理先写入mq,成功写入mq后,将成功的结果返回给用户,后续对mq中的消息进行处理。

削峰:这个可以和异步结合起来理解。比如高并发场景下,大数据大流量直接访问数据库,会造成数据库的压力过大,这个时候我们就可以先将消息写入mq中,后续再进行数据的处理操作。

重复消费问题?

为什么会有重复消费问题?

比如Kafka,每个数据都会有一个offset来标记,类似序号。当消息被消费者消费后,kafka会定时来提交offset,下次kafka重启时,就会根据offset的标记顺序来继续提供消息。但是系统宕机时,offset还没提交,那么下次系统重启时,就会产生一部分消息的重复消费。

解决方法

保持幂等性就可以了,就是说比如消息是需要向数据库中添加数据,如果重复消费两次了,但是你要保证数据库里只有一条数据就可以。

结合业务考虑:

可以根据某些条件去查询下库里是否有这条数据了,如果有的话,就放弃处理,或者进行update处理。

如果是存储redis的话,那就更好办了,redis的set 天然幂等操作

更复杂的情况也可以在消息中携带一个全局唯一性的ID,根据ID来进行是否重复消费的校验操作。

消息丢失问题

RabbitMQ

对于rabbitmq的话,生产者丢失消息,没有将消息成功写入MQ,这种情况的话,可以开启rabbitmq带的事务,也就是在生产者写入消息之前就开始事务,如果消息没有成功写入MQ,MQ会返回一个异常信息,可以进行回滚,再重试发送消息;成功写入则提交事务。

还有一种类似ack应答机制,开启confirm模式,生产者写入rabbitmq都会有一个唯一性的ID,成功写入MQ后,会返回一个ack消息,告诉你成功写入了,如果写入失败,则会回调nack接口,告诉你消息失败了,你可以选择重试发送消息。

上述两种都是生产者消息丢失,两者的区别在于,事务是同步性的,如果卡在这里,就会一直卡下去。confirm模式则是异步的。

如果是mq本身自己消息丢失怎么办。这种情况的话,rabbitMQ可以开启持久化,将数据持久化到磁盘。

如果是消费者弄丢消息呢?回顾上面的confirm模式,我们可以关闭自动的ack回复,只有在消费者消费成功后,再手动的回复ack消息给生产者。

Kafka

消费者成功获取到了消息,此时kafka提交了offset,但是消费者拿到后还没来得及处理,就挂了,而kafka的offset也变了,所以这条消息也就丢失了。解决办法可以是关掉kafka的自动提交offset,只有当消息成功消费后再手动提交offset。

kafka本身自己丢失消息的情况,也就是leader数据还没同步给follower的时候就挂了,这个时候重新从follower选举出的leader或有数据的缺失,这种情况的解决办法是通过更改配置参数

  • 给 topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本。
  • 在 Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower 吧。
  • 在 producer 端设置 acks=all :这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了。
  • 在 producer 端设置 retries=MAX (很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了。

生产者丢失数据的情况,只要在上面设置了acks=all就不会丢数据了,它保证必须写入成功,不然就会重试。

消息的有序性问题

保证消息的有序性很重要,比如一条数据进行了删增改操作,依次写入了mq,但是如果消费者拿到然后消费顺序是增改删,那不就把数据删除了吗

对于rabbit的话,可以多个queue队列,每个队列对应一个消费者;或者一个queue一个消费者,消费者内部自己维护一个内存队列保证消息的有序性。

对于kafka的话,可以单线程消费,一个topic,一个consumer,一个partition,但是吞吐量很低;可以多个queue,相同的key存入一个queue,然后多个消费者,每个线程只消费一个queue,这样就可以多线程工作,提高吞吐量。

消息堆积问题

如果消息长时间没有消费,产生了堆积怎么办?这种场景可以使,消费者拉取到消息后需要写入到数据库,但是如果此时数据库挂了,消费端挂在这里不动了,那消息就会慢慢积压。

处理方法:扩容。新建一个topic,partition设置为之前的10倍大小,再建好之前10倍大小的queue,自己写一个consumer消费者程序消费这些消息,消费的操作就是将这些消息轮询均匀的写入建好的10倍大小的queue。然后使用10倍的机器来部署消费者程序,每批机器消费一个queue。这相当于将消费者和queue都提高了10倍的效率。

对于RocketMQ,官方给出了消息堆积的解决办法:1.增加消费并行度,也就是增加消费者实例(超过订阅的comsumer是无效的)。2.rocketMQ支持批量消费。3.跳过不重要的消息,实现就是更改offset。

消息丢失

找个不重要的时间,一点一点自己查询恢复吧。

你可能感兴趣的:(笔记,分布式,kafka,消息队列,java,rabbitmq)