2018年,Apache Kafka以一种特殊的设计和方法实现了强语义的exactly-once和事务性。

这篇文章将讲解kafka中exactly-once和事务操作的原理,具体为

(1)exactly-once在kafka中的定义。
(2)数据生产者“幂等操作”,kafka的事务性以及exactly-once实现原理。
(3)exactly-once的流处理。
1. 什么是恰好一次exactly-once
exactly-once定义为: 不管在处理的时候是否有错误发生,计算的结果(包括所有所改变的状态)都一样。

所以,在计算的时候如果发生了一个错误,系统重新计算,重新计算的结果和没有错误发生所得到的结果是一样的,因为这些计算操作是“恰好一次的”。这有另外一个专业术语:“幂等操作”。

为什么exactly-once那么重要呢?(1)在流处理操作中,很多应用场景必须需要“恰好一次”的支持。比如生活着有一个很重要的使用场景:在转账给朋友的时候,用户只希望一次转账,如果不支持“恰好一次”,那么就无法保障在违背用户本意的情况下重复转账。(2)对于kafka而言,其是流处理平台的核心部件,因为kafka通常作为公司内部的消息系统中间件,是其他系统的消息传输的桥梁。(3)支持exactly-once操作可以解锁更过的应用,比如金融行业应用。

使用Kafka进行流处理通常主要包含三个步骤:根据topic读取数据 - 流操作 - 将结果保存到指定的topic下。Kafka的流处理支持无状态的流操作(stateless)和有状态的流操作(stateful),无状态的意思是流处理的时候只需要针对某一条消息进行处理,结果只受到这条消息的影响,比如在每一条消息后面追加字符“a”;有状态指的是在消息处理的时候需要保存前后多条消息的相关信息,结果受到多条消息的影响,比如count,average操作。所以有状态操作更加强大但是实现起来更加困难,特别是当它也支持“恰好一次”的时候。

如果不支持exactly-once操作,那么可能出现下面的错误:
(1)重复写入。下图所示左边为输入数据,中间为数据处理,右边是结果写入。现在计算出了结果并且成功写入,但是由于某些原因,系统没有正确识别成功写入结果这个信号,所以系统重试了,这样就导致了下面第二张图所示的结果:也就是计算结果重复写入。

(2)计算状态被多次更新。
如下图所示,箭头所指的是一个有状态的操作(前面已经讲到无状态操作和有状态操作),第一次计算的时候更新了该处的状态。那么如果因为某些原因第一次的计算有问题需要重新计算,箭头所指的状态会被再次更新,从而导致最终的计算结果不正确。因为正确的计算是状态只被更新一次。这里所说的状态似乎有一点抽象,举一个例子,在统计操作中,count++,可以表示成一个状态,每次来一个数据,就增加1个量。

(3)重复读入
第一个数据已经顺利读取,处理和结果写入,但是由于数据读取的原因,系统没有正确识别到第一次数据的读取,所以再次读取了相同的数据,再次计算并输出结果。此时如下面第二张图所示。这样同样的输入数据就产生了两个结果写入,而且如果中间的流操作是有状态的,这两个结果很可能是不一样的。

上面所阐述的问题,进一步说明了exactly-once的重要性。kafka提供了自己的exactly-once保证。

2. 要么都做,要么都不做
要么都做,要么都不做。做什么呢?体现在:(1)写出所有的计算结果 (计算结果写入到kafka指定的topic中).(2)所有状态的更新。(3)把输入的消息标记为已消费(这里的输入数据理解为kafka的消费者从broker中pull数据)。对上面这三个,kafka使用另一种具有相同语义的方式表示,分别为:(1)将计算结果写入输出topic中(2)把更新操作写入“更新日志changelog”中(注意,操作的状态能够根据“更新日志”进行回滚,类似于MySQL的更新日志,这个有别于普通的系统操作日志)(3)把消费的消息偏移量写入相应的topic中。这也就是Apache Kafka实现“要么都做,要么都不做”和exactly-once的总体设计思路。

具体地,上面阐述关系到三个操作,分别为:

消息生产者提交数据到broker
broker进行消息处理,
消费者消费数据
对于第一点,需要实现幂等操作以及多分区地原子写入。这里的“写入”指的是消息的producer向broker传入消息。这里不多讲“幂等”操作,可以简单理解为同一个消息,producer一次或者多次重复向broker传输,对broker的影响是一样的。多分区原子写入指的是,producer将多条消息一次向broker中的多个partition传输,原子性体现在要么这些消息都成功传入了,要么都没有传入。
在下面第一张图片中展示了kafka实现消息传输的幂等操作的原理。每一条消息除了消息的key和消息的值,还增加了两个字段,分别是producer的ID和一个全局唯一的序列号。这个序列号由broker生成,类似于流水号。在图片中,闪电表示消息的ack失败,消息重传,kafka根据消息的pid和seq来判断这条消息是否已经传过。因为pid和seq也同消息一样存在kafka的patition中的,所以不需要当心丢失问题。要启动kafka的幂等性,无需修改代码,默认为关闭,需要修改配置文件:enable.idempotence=true 同时要求 ack=all 且 retries>1。

对于第二点,需要实现:将模式“消息读入->消息处理->结果写出”作为事务操作,并且整个操作满足exactly-once。所谓的事务操作,也就是这个操作需要满足原子性,完整性,一致性和持久性。kafka在支持事务性的同时也保证了系统性能,这体现在它简单但是高效的设计和实现上面。下面分析kafka实现事务的原理。在下面第一张图片中,左下角表示事务日志,系统存在一个事务锁,在某一个事务开始之前需获取这个锁。左上角的T1,T2表示两个topic,P1和P2表示两个partition,也就是这个事务往两个不同的topic和两个不同的partition上面存储数据。看图片的右上角,第一行代码,首先告诉系统要开始一个事务,接着发送消息到broker相应topic和partition中,所有都正常且完成之后,提交这个事务。所有的这个过程都另外有相应的log。只有成功完成了这个事务之后,消费者才能消费这个事务所提交的消息。

实现事务的回滚需要借助changelogs的帮助,如下面第一张图片所示。changelogs是存储在相应的topic中。

对于第三点,需要实现,kafka消费者只读取已经标记为“成功提交”的数据,这句话隐含了另外一层意思,消息提交的状态有多种,而成功提交只是其中之一。这里的“提交”指的是producer向broker提交的消息。那么什么才能算是成功提交了呢?消息被partition的leader和其所有的follower成功记录了,才能算是成功提交了。成功提交所带来的好处就是不怕断电不怕机器故障,也就是高容错性。下面图片展示了kafka如何解决消费者重复读的问题。(1)消息的消费,(2)消息的处理,(3)把消息的处理结果发送到某一个topic中和(4)把偏移量的发送某一个topic中,它们被放到一个事务中,当所有这些成功之后,才能算是成功。注意到,消费者的偏移量是使用一个producer发送的,也就是把偏移量当成了一种消息在kafka集群中保存起来。这样的话,只要这个事务完成了,那么偏移量也成功保存了。

所以,对应下面第一张图片,不仅仅有changelogs,还有__consumer_offsets

默认情况下kafka的事务是关闭的,通过配置文件开启,需要
transactional.id=“unique-id”, 要求enable.idempotence=true.

启动exactly-once需要配置:processing.guarantee="exactly-once ", 默认是最少一次。

3. 脏数据
脏数据指的是producer把消息数据提交到了broker中,但是它们没有成功,此时这些数据依然存在broker中。为了避免让消费者消费这些脏数据,kafka设置了消息的隔离等级,可以通过配置文件,指定只有成功提交的数据才能被消费。配置为isolation.level=“read_committed”。默认是read_uncommitted

kafka exactly-once的更多相关文章

  1. Spark踩坑记——Spark Streaming+Kafka

    [TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...

  2. 消息队列 Kafka 的基本知识及 .NET Core 客户端

    前言 最新项目中要用到消息队列来做消息的传输,之所以选着 Kafka 是因为要配合其他 java 项目中,所以就对 Kafka 了解了一下,也算是做个笔记吧. 本篇不谈论 Kafka 和其他的一些消息 ...

  3. kafka学习笔记:知识点整理

    一.为什么需要消息系统 1.解耦: 允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 2.冗余: 消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险. ...

  4. .net windows Kafka 安装与使用入门(入门笔记)

    完整解决方案请参考: Setting Up and Running Apache Kafka on Windows OS   在环境搭建过程中遇到两个问题,在这里先列出来,以方便查询: 1. \Jav ...

  5. kafka配置与使用实例

    kafka作为消息队列,在与netty.多线程配合使用时,可以达到高效的消息队列

  6. kafka源码分析之一server启动分析

    0. 关键概念 关键概念 Concepts Function Topic 用于划分Message的逻辑概念,一个Topic可以分布在多个Broker上. Partition 是Kafka中横向扩展和一 ...

  7. Kafka副本管理—— 为何去掉replica.lag.max.messages参数

    今天查看Kafka 0.10.0的官方文档,发现了这样一句话:Configuration parameter replica.lag.max.messages was removed. Partiti ...

  8. Kafka:主要参数详解(转)

    原文地址:http://kafka.apache.org/documentation.html ############################# System ############### ...

  9. kafka

    2016-11-13  20:48:43 简单说明什么是kafka? Apache kafka是消息中间件的一种,我发现很多人不知道消息中间件是什么,在开始学习之前,我这边就先简单的解释一下什么是消息 ...

  10. Spark Streaming+Kafka

    Spark Streaming+Kafka 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端, ...

随机推荐

  1. assign, retain, copy, weak, strong

    一.assign, retain, copy 的区别(引用计数 RC reference count) 参考:IOS基础:retain,copy,assign及autorelease 1. 假设你用m ...

  2. MySQLfailover错误一则

    由于公司现有主库要转移到新的主库上,所以,我打算利用MySQLfailover工具的故障转移. 1.开发把程序账号转移到新主库上 2.停止现有主库,使之进行故障转移,转移期间会自动锁表,保持数据一致性 ...

  3. (4)zabbix监控第一台服务器

    2. zabbix监控服务器 创建主机,选择模板以及录入基本信息,过一分钟左右,就可以看到cpu.内存.硬盘等等使用情况.本节以图文为主.by the way, zabbix中文翻译很烂,config ...

  4. linux定时任务执行php任务

    首先用命令检查服务是否在运行 systemctl status crond.service 如果服务器上没有装有crontab ,则可以执行 yum install vixie-cron yum in ...

  5. python基础知识14-正则表达式

    1.正则表达式 正则可以代替其他任何工具,但是其他工具不能完全代替正则. 1.匹配或提取字符串的工具,基于所有语言之上的工具. 正则表达式所面向的问题 判断一个字符串是否匹配给定的格式,如判断用户注册 ...

  6. 关于get_magic_quotes_gpc()函数

    function sqlReplace($str) { $strResult = $str; if(!get_magic_quotes_gpc()) //如果 gpc 没有开的话 { $strResu ...

  7. Verilog学习笔记基本语法篇(一)·········数据类型

    Verilog中共有19种数据类型. 基本的四种类型: reg型.wire型.integer型.parameter型. 其他类型:large型.medium型.small型.scalared型.tim ...

  8. LDAP学习小结【仅原理和基础篇】

    此篇文章花费了好几个晚上,大部分是软件翻译的英文文档,加上自己的理解所写,希望学习者能尊重每个人的努力. 我有句话想送给每个看我文章的人: 慢就是快,快就是慢!!! 另外更希望更多人能从认真从原理学习 ...

  9. python基础学习笔记——os模块

    #OS模块 #os模块就是对操作系统进行操作,使用该模块必须先导入模块: import os #getcwd() 获取当前工作目录(当前工作目录默认都是当前文件所在的文件夹) result = os. ...

  10. python的re模块常用方法

    正则表达式模式 模式字符串使用特殊的语法来表示一个正则表达式: 字母和数字表示他们自身.一个正则表达式模式中的字母和数字匹配同样的字符串. 多数字母和数字前加一个反斜杠时会拥有不同的含义. 标点符号只 ...