kafka的事务指的是2个点   ① 生产者到kafka服务端的事务保障    ②消费者从kafka拉取数据的事务

kafka提供的事务机制是 第①点,  对于第②点来说 只能自己在消费端实现幂等性。

我们来介绍第①点, 因为生产者producer写到kafka可能会出现消息重复,比如 设置ack=all,写入到kafka的leader时,leader挂掉了,

没有及时反馈ack,导致生产者再次发送消息就会出现重复消息落盘。这种情况可以设置kafka的属性用来开启幂等。但是这种幂等

只能保证 producer没有挂掉的情况下,因为幂等的原理是 kafka缓存了一份 pid,partition,seqnumber 的数据,如果命中则说明之前缓存了,

但是如果producer挂掉了重启后,它的pid就会变化,partition也有可能变化,就会导致消息会出现重复状况。所以kafka 0.11版本加入了事务机制

开启时事务后,会存在 transaction_id , 封装成( transaction_id, pid,partition,seqnumber, 消费到哪条记录等等) 保存在kafka上,如果producer 挂了重新

启动的时候,会自动寻找kafka中的这个 transaction_id,找到的话就会恢复到挂掉之前的状态 ,然后进行消费。kafka事务保证了  要么全部成功,要么全部失败。

还有一个很重要的点是 要在consumer端 设置   isolation.level 为 read_committed状态,它默认是read_uncommitted状态,这是什么意思呢? 接下来详细说明一下:

目前producer是双线程设计,后台的Sender线程负责实际的消息发送。当Sender线程构造消息batch发送时,它会尝试去读取事务状态,如果发现已经abort,则立即将未发送的batch全部fail掉——这就是为什么你注释Thread.sleep后则不能发送的原因。当你加入了Thread.sleep之后batch发送时主线程在休眠,尚未执行到abortTransaction,故Sender线程成功地发送了消息到Kafka broker。

另外,你需要为consumer端配置isolation.level = read_committed。这样不管哪种情况你都不会读取到任何未提交的消息。默认是read_uncommitted,即使abort的消息,只要是成功发送到Kafka了,consumer就能读取到。

1、也就是开启事务之后,生产者调用send发送数据时他就会直接向kafka插入数据,只不过是这个数据后面追加了一个状态,这个状态是read_uncommited代表未提交,只有producer调用了commitTransaction时候 这些数据在kafka中才会都标记为read_commited。

所以 如果在 consumer消费方没有设置 isolation.level 为 read_committed状态(默认是read_uncommited),那么当producer 出现 异常或者宕机或者在事务提交之前发送的数据也依然能读取到,因为前面说了 这些数据是send的时候依然插入到kafka中只不过状态标记为uncommited而已,所以要想实现事务,还得在consumer方设置 隔离级别 isolation.level 为 read_committed,表示只能读取提交状态的记录,这样不管在任何条件下都不会读取到任何未提交的消息
 
2、还有一个情况是,当生产者中代码捕获到了异常,并进行abortTransaction,而消费者并没有设置隔离级别为read_committed,但却读不到消息呢,那我们可以 想象成当生产者调用abortTransaction时接下来的消息肯定不会发送到服务器,并且已发送到服务器上的
消息还会直接删掉,这样理解就可以了
 

producer实现代码如下:

public class producer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
Properties props = new Properties();
props.put("bootstrap.servers","hadoop102:9092,hadoop103:9092,hadoop104:9092");
props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
props.put("acks","all");
props.put("retries","2");
props.put("batch.size","16384");
props.put("transactional.id","tran-wb2"); //事务ID,开启事务下面幂等也要开启
props.put("enable.idempotence", "true"); //开启幂等
// 一定要在消费者方设置 isolation.level为 read_committed,表示只读取已提交事务状态的记录
Producer<Object, Object> producer = new KafkaProducer<>(props); producer.initTransactions();
producer.beginTransaction();
try {
for (int i = 0; i <100 ; i++) {
Future<RecordMetadata> first = producer.send(new ProducerRecord<>("first", i + "sad ", i + 1 + "s d"));
//first.get(); 加上get可以实现同步发送操作
if (i==20){
throw new RuntimeException("测试异常回滚");
}
}
} catch (RuntimeException e){
System.out.println(e.toString());
producer.abortTransaction(); //出现异常,就进行回滚,这样所有消息都会失败
producer.close();
return;
} producer.commitTransaction(); //没有异常就 事务提交
producer.close();
}
}

消费者代码

public class consumer {

    public static void main(String[] args) throws InterruptedException {

        Properties properties = new Properties();
properties.put("bootstrap.servers", "hadoop102:9092,hadoop103:9092,hadoop104:9092");
properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
properties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
properties.put("group.id", "wangbingsaa");
properties.put("isolation.level", "read_committed"); //一定要设置 只拉取 已提交事务状态的记录,这样无论什么条件都可以
// properties.put("auto.offset.reset","earliest"); //设置拉取的位置
properties.put("enable.auto.commit", "false"); //关闭自动提交
properties.put("auto.commit.interval.ms", "1000"); //自动提交间隔 Consumer<String, String> consumer = new KafkaConsumer<>(properties); consumer.subscribe(Collections.singletonList("first")); ConsumerRecords<String, String> records = consumer.poll(4000); //如果拉取时长超过4000毫秒 就不拉取
for (ConsumerRecord<String, String> record : records) {
System.out.printf("offset = %d, partitoon = %d key = %s, value = %s%n", record.offset(), record.partition(),record.key(), record.value());
}
consumer.commitSync(); //手动提交
}
}

kafka 事务代码实现(生产者到server端的事务)的更多相关文章

  1. HBase 协处理器编程详解第一部分:Server 端代码编写

    Hbase 协处理器 Coprocessor 简介 HBase 是一款基于 Hadoop 的 key-value 数据库,它提供了对 HDFS 上数据的高效随机读写服务,完美地填补了 Hadoop M ...

  2. JS学习十四天----server端运行JS代码

    server端运行JS代码 话说,当今不在client使用JS代码才是稀罕事.因为web应用的体验越来越丰富,client用JS实现的逻辑也越来越多,这造成的结果就是某些差点儿一致的逻辑须要在clie ...

  3. 上机题目(0基础)- Java网络操作-Socket实现client和server端通信二(Java)

    上一节实现了client像server端发送请求.本节将实现server端向client回传信息.实现原理非常easy,在原来的基础上.在server端实现输出流,在client实现输入流就可以,详细 ...

  4. golang如何优雅的编写事务代码

    目录 前言 需求 烂代码示例 重构套路 一.提前返回去除if嵌套 二.goto+label提取重复代码 三.封装try-catch统一捕获panic 前言 新手程序员概有如下特点 if嵌套特别多.重复 ...

  5. Kafka源码分析(三) - Server端 - 消息存储

    系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 业务模型 1.1 概念梳理 1.2 文件分析 1.2.1 数据目录 1.2.2 . ...

  6. Hadoop基于Protocol Buffer的RPC实现代码分析-Server端

    http://yanbohappy.sinaapp.com/?p=110 最新版本的Hadoop代码中已经默认了Protocol buffer(以下简称PB,http://code.google.co ...

  7. Hadoop基于Protocol Buffer的RPC实现代码分析-Server端--转载

    原文地址:http://yanbohappy.sinaapp.com/?p=110 最新版本的Hadoop代码中已经默认了Protocol buffer(以下简称PB,http://code.goog ...

  8. 搭建分布式事务组件 seata 的Server 端和Client 端详解(小白都能看懂)

    一,server 端的存储模式为:Server 端 存 储 模 式 (store-mode) 支 持 三 种 : file: ( 默 认 ) 单 机 模 式 , 全 局 事 务 会 话 信 息 内 存 ...

  9. Linux下的C Socket编程 -- server端的继续研究

    Linux下的C Socket编程(四) 延长server的生命周期 在前面的一个个例子中,server在处理完一个连接后便会立即结束掉自己,然而这种server并不科学啊,server应该是能够一直 ...

随机推荐

  1. 一起来找茬:记一起 clang 开启 -Oz 选项引发的血案

    作者:字节跳动终端技术 -- 刘夏 前言 笔者来自字节跳动终端技术 AppHealth (Client Infrastructure - AppHealth) 团队,在工作中我们会对开源 LLVM 及 ...

  2. Spring专题2: DI,IOC 控制反转和依赖注入

    合集目录 Spring专题2: DI,IOC 控制反转和依赖注入 https://docs.spring.io/spring/docs/2.5.x/reference/aop.html https:/ ...

  3. jstl split 分割字符串?

    1. 在已经获得数据的前提下,对数据进行分割,分割后的结果是一个数组,可以通过数组获得我们需要的数据 2. value 是获得值 需要${} 的方式获取, var 是设置变量. var = " ...

  4. Transformer可解释性:注意力机制注意到了什么?

    原创作者 | FLPPED 论文: Self-Attention Attribution: Interpreting Information Interactions Inside Transform ...

  5. IDEA学习系列之Module概念

    感谢原文作者:小manong 原文链接:https://www.jianshu.com/p/fcccc37fcb73 简单应用:IDEA Maven创建多个Module相互依赖 1.Module的概念 ...

  6. 前端也能做AI

    殷圣魁 58架构师 7月16日 原文链接 前言 相信不少人看过一篇人工智能已经能实现自动编写HTML,CSS的文章,人工智能开始取代前端的一部分工作.前端开发行业真的被人工智能取代吗? 1.人工智能发 ...

  7. Linux Shell脚本攻略复习

    1. 打开终端后的提示符中,$表示普通用户,#表示管理员用户root,root是linux系统中权限最高的用户. 2. shell脚本通常是一个#!起始的文本文件,其中#!位于解释器路径之前. 例如: ...

  8. #import和#include区别

    #import与#include的类似,都是把其后面的文件拷贝到该指令所在的地方 #import可以自动防止重复导入 #import <> 用于包含系统文件 #import"&q ...

  9. java创建一个子类对象是会调用父类的构造方法会不会创建父类

    1.子类在创建实例后,类初始化方法会调用父类的初始化方法(除了Java.lang.Object类,因为java.lang.Object类没有父类),而这种调用会逐级追述,直到java.lang.Obj ...

  10. java常用类,包装类,String类的理解和创建对象以及StringBuilder和StringBuffer之间的区别联系

    一.包装类的分类: 1.黄色部分的父类为Number 继承关系: Boolean Character 其他六个基本数据类型 2.装箱和拆箱 理解:一个例子,其他的都相同 装箱:Integer inte ...