消息队列的可靠性

2020-08-04 21:22:24 浏览数 (1)

RabbitMQ可能存在的数据丢失问题

数据丢失分为三类:

  1. 生产者:生产者写消息存在丢失或者MQ接受存在问题
  2. MQ : MQ接受到数据先暂存在内存中,消费者还没有消费,MQ挂掉,缓存丢失。
  3. 消费者:消费拉取到消息还没处理直接挂掉,但MQ以为对方收到消息。
解决方法

生产者: rabbitMQ支持事务,可以在发送中进行捕获异常,如果出现未接受异常进行回滚操作。

但是rabbitMQ事务操作太耗费性能,因为为了保证可靠性,需要同步等待机制,等待你成功。

confirm机制,先设置 channel 为confirm,如果接受到消息,回调这个接口,接受成功。如果没接收,调用回调接口,接收失败。

MQ : 防止MQ突然挂掉内存数据丢失,就需要将MQ持久化到磁盘。第一个是创建queue的时候将其设置为持久化的,这样就可以保证rabbitmq持久化queue的元数据,但是不会持久化queue里的数据;第二个是发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化的,此时rabbitmq就会将消息持久化到磁盘上去。

消费者 : rabbitmq如果丢失了数据,主要是因为你消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,那么就尴尬了,rabbitmq认为你都消费了,这数据就丢了。

这个时候得用rabbitmq提供的ack机制,简单来说,就是你关闭rabbitmq自动ack,可以通过一个api来调用就行,然后每次你自己代码里确保处理完的时候,再程序里ack一把。这样的话,如果你还没处理完,不就没有ack?那rabbitmq就认为你还没处理完,这个时候rabbitmq会把这个消费分配给别的consumer去处理,消息是不会丢的。

Kafka可能存在的数据丢失问题

消费者 : 唯一可能导致消费者弄丢数据的情况,就是说,你那个消费到了这个消息,然后消费者那边自动提交了offset。关闭自动提交offset,在处理完之后自己手动提交offset,就可以保证数据不会丢。同时自己要用程序保证幂等性。

kafka : kafka某个broker宕机,然后重新选举partiton的leader时。大家想想,要是此时其他的follower刚好还有些数据没有同步,结果此时leader挂了,然后选举某个follower成leader之后,他不就少了一些数据.

所以此时一般是要求起码设置如下4个参数:

给这个topic设置replication.factor参数:这个值必须大于1,要求每个partition必须有至少2个副本

在kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系,没掉队,这样才能确保leader挂了还有一个follower吧

在producer端设置acks=all:这个是要求每条数据,必须是写入所有replica之后,才能认为是写成功了

在producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了

我们生产环境就是按照上述要求配置的,这样配置之后,至少在kafka broker端就可以保证在leader所在broker发生故障,进行leader切换时,数据不会丢失

生成者 : 如果按照上述的思路设置了ack=all,一定不会丢,要求是,你的leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

保证消息队列顺序性

rabbitmq:一个queue,多个consumer。因为多个 消费者是并行运行,最后的结果可能会乱序。

解决办法: 就是为每一个机器设置一个queue,然后如果要求有序的数据必须确保发到同一个queue中,那么数据的接受就不会乱序。

kafka:一个topic,一个partition,一个consumer,内部多线程.

kafka写到一个partition中的数据一定是有数据的。

解决办法: 一个topic,一个partition,一个consumer,内部单线程消费,写N个内存queue,然后N个线程分别消费一个内存queue即可,既消费者不要使用多线程进行处理。或者你想多线程进行处理,你可以建立一个内存队列,通过hash进任务id相同分到一个内存队列,每一个内存队列对应一个线程。

0 人点赞