设为首页 加入收藏

TOP

kafka消息交付语义的分析
2019-04-23 14:31:44 】 浏览:54
Tags:kafka 消息 交付 语义 分析
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jeffrey11223/article/details/80775080

在kafka中,在producer和consumer这两个维度上都有三种消息交付的语义:

At most once ---- 消息可能会丢失但绝不重传.
At least once ---- 消息可以重传但绝不丢失.
Exactly once ---- 每一条消息只被传递一次.

先来看producer

producer设置中有这么一个选项:

每发送一次消息,都会要求broker返回一个消息回执,即ack。如果ack没有收到,producer会进行重发,如果设置了重发次数的话。这个ack有三种模式:

// The level of acknowledgement reliability needed from the broker (defaults
// to WaitForLocal). Equivalent to the `request.required.acks` setting of the JVM producer.
// 等同于jvm kafka中的`request.required.acks` 
        RequiredAcks RequiredAcks

type RequiredAcks int16
const (
// 第一个模式,NoResponse doesn't send any response, the TCP ACK is all you get.
    NoResponse RequiredAcks = 0
//第二个模式, WaitForLocal waits for only the local commit to succeed before responding.
    WaitForLocal RequiredAcks = 1
// 第三个模式,WaitForAll waits for all in-sync replicas to commit before responding.
// The minimum number of in-sync replicas is configured on the broker via
// the `min.insync.replicas` configuration key.
    WaitForAll RequiredAcks = -1
)

如果RequiredAcks设置为0,在这种情况下,服务器是否收到请求是没法保证的,并且参数retries(重发)也不会生效(因为客户端无法获得失败信息)。此时提供的是At most once的语义。

如果RequiredAcks大于0,producer在没有收到应答的情况下,会进行重发,此时提供的是At least once的语义。

幂等性保证Exactly once

在kafka 0.11.0.0之前,是无法保证Exactly once的,但从0.11.0.0开始,producer引入了幂等性的概念,保证消息只会被传递一次。

那么kafka是如何实现的呢,用到了Producer ID(即PID)和Sequence Number。

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

Broker端在缓存中保存了Sequence Numbler,对于接收的每条消息,如果其序号比Broker缓存中的序号大于1则接受它,否则将其丢弃,这样就可以防止了消息重复提交。
但是,以上只能保证单个Producer对于同一个[ Topic, Partition ]的Exactly Once语义。不保证同一个Producer一个topic下不同Partition的幂等。

事务保证Exactly once

从0.11.0.0开始,kafka支持了producer事务。要注意的一点是,不要把操作db的业务逻辑跟操作消息当成一个事务,其实是有问题的,因为操作DB数据库的数据源是DB,消息数据源是kfaka,是完全不同两个数据,一种数据源(如mysql,kafka)对应一个事务,所以它们是两个独立的事务。kafka事务指kafka一系列 生产、消费消息等操作组成一个事务。db事务是指操作数据库的一系列增删改操作组成一个事务。

对于producer和生产消息来说,如果是只有写,即一条消息要发送给多个topic,可以使用producer事务来保证要么都发送了,要么就都没有发送。

如果有消费消息然后再发送给别的topic,最后提交offset,也可以使用producer事务来保证这一系列操作的原子性。比如消费者提交offset出现问题,导致consumer在重复消费消息的时候,生产者会重复的生产消息给另外的消费者。

如果只是消费消息和提交offset,producer事务就显得没有意义了,因为这个和手动提交offset没有什么区别。

现在再从consumer角度来看

Consumer 读取到消息之后,先进行offset提交,然后再处理消息,如果消息处理到一半失败了,那这条消息就再也不会被消费了,这对应于at-most-once的语义。

Consumer 读取到消息之后,先处理消息,最后再offset提交。这样如果处理消息成功,在offset提交之前服务崩溃了,那么重启之后这条消息会再次被消费到,这对应于at-least-once的语义。

如果要Exactly once语义,则可以使用如下手段:
消费处理失败指的是业务失败或者操作db失败。
消费处理成功指的是业务成功或者操作db成功。
1,如果消费处理失败的话需要额外记录此条消息的offset,对于有顺序要求的消费来说,此时还得停止消费。下次再统一去消费这些处理失败的offset的消息。
2,同样消费处理失败,也可以利用producer事务来保证,比如提交offset并且把offset发送到另一个topic中,来保证这一系列的原子性,消费处理失败了,则中断事务,offset就不会被发送到topic中,topic中保存的还是上次那个offset。
3,如果消费处理成功,需要额外保存最新提交的offset到文件系统中,然后提交offset。这样不管offset提交成功还是失败,重启之后都可以从文件中拿到最新的offset。
4,或者,消费处理成功的同时,比如db操作成功的同时,把offset写到db中,意思就是consumer将offset存储和其输出在相同的位置。然后提交offset。这样我的输出位置保存的也是最新最准确的offset。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇kafka:spark-project项目的kafka.. 下一篇kafka LeaderNotAvailableExcepti..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目