所谓可靠性传输是指:不能丢数据。这里丢数据分两种:
- 生产者生产时丢数据
- 消息队列本身丢数据
- 消费者消费时丢数据
RabbitMQ
RabbitMQ 有三种丢数据的情况:
- 生产者传入过程中消息丢失
- RabbitMQ 收到消息,暂时保存内存,还没来得及消费,进程挂掉了,内存中数据丢失
- 消费者消费 MQ 时,正准备处理,系统宕机了,RabbitMQ 认为这条 MQ 已经被消费
RabbitMQ 生产者在发送数据前可以开启 RabbitMQ 事务,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,回滚事务重新发送即可。如果消息被成功接收,提交事务即可。然而使用事务会大幅度降低吞吐量,系统损耗严重
除了上面提到的事务处理,还可以采用 confirm 模式。在生产者设置开启 confirm 模式后,每次写消息会分配唯一 ID,消息写入 RabbitMQ 时会回传 ack 消息,如果没有处理某个消息,RabbitMQ 会回调 nack 接口,告诉某个消息发送失败,生产者可以重发。甚至生产者可以在内存中根据 ID 维护每个 MQ 的状态,如果长时间没有收到 ack 也重发
这里事务是同步的,confrim 是异步的,相比事务,confirm 更加高效
对于 RabbitMQ 自己丢失数据,可以通过持久化解决,消息写入之后会持久化到磁盘,此时哪怕 RabbitMQ 挂掉了,恢复之后也可以自动读取之前存储的数据,不会丢失
如果 RabbitMQ 在正准备持久化时挂掉,就可能丢失一小部分数据。此时我们可以结合 confrim 模式,在消息持久化完成后再返回 ack,丢失的数据生产者会重发。
消费端弄丢数据的主要是因为正准备消费时,系统挂掉了。此时我们关闭 RabbitMQ 自动 ack 机制,不在消费端收到消息时返回 ack,而在消费端处理完消费无误时再 ack,这样即使处理 MQ 时系统异常宕机也不会影响数据。
Kafka
KafKa 同样有三种丢数据的场景:
- 生产者传送时丢失
- Kafka 自身丢失
- 消费者消费时丢失
Kafka 可以通过以下两个参数确保生产者传送时不丢失数据:
- acks=all:每条数据写入所有 leader 以及 follower 才会返回 ack
- retries=MAX:确保每次写入失败后,无限重试
Kafka 自身丢数据主要集中在某个 broker 突然挂机,此时该 broker 上的 leader partition 还没有完全同步到 follower,后续重新选主出的 leader 就会丢失一部分数据。对于这种场景通过以下四个参数解决:
- topic 设置 replication.factor,值大于1,要求每个 partition 至少有两个副本
- 给 kafka 设置 min.insync.replicas,值大于1,保证一个 leader 至少感知到一个 follower 跟自己保持一致。
- (配合生产者传送时丢失的两个参数)
对于消费者消费时丢失,原理和 RabbitMQ 是相同的,无非 RabbitMQ 接收 ack,Kafka 返回 offset。处理方式也相同:关闭 Kafka offset 自动提交,在代码中消息正常消费完成后再提交 offset 即可。