消息中间件可靠性和幂等性

问题1:如何消息不丢失

如何保障消息中间件 100% 消息投递成功?在kafka中可以通过ack参数配置实现:

  • 参数为0:producer不等待ack,若beoker还未写入就返回,会造成数据丢失。
  • 参数为1: leader写入成功后返回ack,不等待follower同步成功;
  • 参数为-1:等待全部folleower落盘成功后返回。

消息中间件可靠性和幂等性_第1张图片

ack机制并不能保证消息100%不丢失,原因如下:

如果我们生产者每发一条消息,都要MQ持久化到磁盘中,然后再发起ack或nack的回调。这样的话是不是我们MQ的吞吐量很不高,因为每次都要把消息持久化到磁盘中。 写入磁盘这个动作是很慢的。这个在高并发场景下是不能够接受的,吞吐量太低了。

所以MQ持久化磁盘真实的实现,是通过异步调用处理的,他是有一定的机制,如:等到有几千条消息的时候,会一次性的刷盘到磁盘上面。而不是每来一条消息,就刷盘一次。

所以comfirm机制其实是一个异步监听的机制 ,是为了保证系统的高吞吐量 ,这样就导致了还是不能够100%保障消息不丢失,因为即使加上了confirm机制,消息在MQ内存中还没有刷盘到磁盘就宕机了,还是没法处理。

解决办法:消息提前持久化 + 定时任务

消息中间件可靠性和幂等性_第2张图片

流程说明:

  1. 订单服务生产者再投递消息之前,先把消息持久化到Redis或DB中,建议Redis,高性能。消息的状态为发送中。
  2. confirm机制监听消息是否发送成功?如ack成功消息,删除Redis中此消息。
  3. 如果nack不成功的消息,这个可以根据自身的业务选择是否重发此消息。也可以删除此消息,由自己的业务决定。
  4. 这边加了个定时任务,来拉取隔一定时间了,消息状态还是为发送中的,这个状态就表明,订单服务是没有收到ack成功消息。
  5. 定时任务会作补偿性的投递消息。这个时候如果MQ回调ack成功接收了,再把Redis中此消息删除。

问题2:如何保证消息没有重复发送?

为了实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number。

  • PID。每个新的Producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
  • Sequence Numbler。(对于每个PID,该Producer发送数据的每个都对应一个从0开始单调递增的Sequence Number

Kafka可能存在多个生产者,会同时产生消息,但对Kafka来说,只需要保证每个生产者内部的消息幂等就可以了,所有引入了PID来标识不同的生产者。
对于Kafka来说,要解决的是生产者发送消息的幂等问题。也即需要区分每条消息是否重复。
Kafka通过为每条消息增加一个Sequence Numbler,通过Sequence Numbler来区分每条消息。每条消息对应一个分区,不同的分区产生的消息不可能重复。所有Sequence Numbler对应每个分区
Broker端在缓存中保存了这seq number,对于接收的每条消息,如果其序号比Broker缓存中序号大于1则接受它,否则将其丢弃。这样就可以实现了消息重复提交了。但是,只能保证单个Producer对于同一个的Exactly Once语义。不能保证同一个Producer一个topic不同的partion幂等。

你可能感兴趣的:(业务)