这个问题也可以换一种问法:如何处理消息丢失的问题?

    使用mq有个基本原则,数据不能多一条,也不能少一条。

    1. RabbitMQ(三个角度)
      ① 生产者弄丢了数据:
      生产者在发送数据到MQ的过程中,可能由于网络问题,在半路就搞丢了。
      方法一:
      此时可以选用RabbitMQ提供的事务功能,生产者发送消息之前打开一个事务,channel.txSelect,如果mq没接到消息,那么生产者会报错,回滚事务,再去重试,再去发送。
      事务机制是同步的,生产者发送消息会等待成功或失败,导致吞吐量下降。
      方法二:
      先把channel设置成confirm模式,然后发送消息给mq,rabbitmq如果接收到这条消息,就会回调生产者本地接口,告诉生产者已经收到了。
      如果在接收消息时报错,回调接口告诉生产者接收失败了,可以再次重试。
      生产者需要提供一个供回调的回调接口的实现,如ack/nack,在nack中再重发一次这个消息。
      异步模式,不会阻塞,吞吐量会高一些。
      ② RabbitMQ弄丢了数据:
      持久化也会有可能丢数据,刚收到的消息还没写到磁盘,MQ挂了。
      两个步骤:
      第一个是创建queue是将其设置成持久化的,这样保证rabbitmq持久化queue的元数据,但不会持久化queue里的数据。
      第二个是发送消息时将消息的deliveryMode设置为2,就是将消息设置为持久化的。
      必须同时设置这两个持久化才行,而且持久化可以跟生产者那边的confirm机制配合使用,当消息被写入磁盘后在返回ack。
      ③ 消费端弄丢了数据:
      出现这个问题,是因为打开了消息消费者的autoack机制,接到消息之后,自动通知rabbitmq说已经处理完了,消费者层面需要将autoack关闭,手动发送ack,防止宕机。

    2. kafka
      ① 消费者弄丢数据:
      原因,消费到这个消息,自动提交offset,需要手动提交offset,避免挂掉。
      此时只要将自动offset关闭,在处理完成后手动提交offset,保证数据不丢失。
      ② kafka自己弄丢数据:
      场景:kafka某个broker宕机,然后重新选举partition的leader是,此时其他follower刚好还有些数据没有同步,然后leader挂了,选举某个follower之后,他就少了一些数据,造成数据丢失。
      此时一般是要求起码设置如下四个参数:
      A、给topic设置replication.factor参数,这个值必须大于1,要求每个partition必须至少有2个副本;
      B、在kafka服务端设置min.insync.replicas参数,这个值必须大于1,要求一个leader至少感知到有至少一个follower还能跟自己保持联系,这样才能保证leader挂了,还有一个follower;
      C、在producer端设置ack= all,要求每条数据,必须是写入所有的replica之后,才能认为是成功了;
      D、在producer端设置retries=MAX,这个要求一旦写入失败,就无限重试,卡在这里了;
      这样配置之后,至少在kafka broker端就可以保证在leader所在的broker发生故障进行leader切换时数据不会丢失。
      ③ 生产者不会弄丢数据:
      ack=all,一定不会丢,leader收到并且所有follower都收到了才会确认返回 。