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. Tomcat服务器和Servlet版本的对应关系

    Tomcat服务器和Servlet版本的对应关系 Servlet 程序从2.5版本是现在世面使用最多的版本(xml配置) 到了Servlet3.0后.就是注解版本的Servlet使用

  2. Java多线程专题6: Queue和List

    合集目录 Java多线程专题6: Queue和List CopyOnWriteArrayList 如何通过写时拷贝实现并发安全的 List? CopyOnWrite(COW), 是计算机程序设计领域中 ...

  3. el表达式中的${param}用法

    el表达式中的${param}? 1. 2. ${param.name} 等价于 request.getParamter("name"),这两种方法一般用于服务器从页面或者客户端获 ...

  4. 微服务架构 | 7.2 构建使用 JWT 令牌存储的 OAuth2 安全认证

    目录 前言 1. JWT 令牌存储基础知识 1.1 JSON Web Token 2. 构建使用 JWT 令牌存储的 OAuth2 服务器 2.1 引入 pom.xml 依赖文件 2.2 创建 JWT ...

  5. 实现表单input文本框不可编辑的三种方法

    感谢原文作者:青灯夜游 原文链接:https://www.php.cn/div-tutorial-413133.html 目录 问题 实现方式 1.οnfοcus=this.blur() 2.read ...

  6. 如何修改TOMCAT的默认主页为你自己项目的主页

    感谢作者:xxs673076773 原文链接:https://www.iteye.com/blog/xxs673076773-1134805 (最合适的) 最直接的办法是,删掉tomcat下原有Roo ...

  7. ListIterator特有的方法

    import java.util.ArrayList; import java.util.List; import java.util.ListIterator; /* 迭代 listIterator ...

  8. Ext原码学习之Ext.js

    1 // JavaScript Document 2 //定义全局Ext变量 3 var Ext = Ext ||{}; 4 Ext._startTime = new Date().getTime() ...

  9. 模型融合——stacking原理与实现

    一般提升模型效果从两个大的方面入手 数据层面:数据增强.特征工程等 模型层面:调参,模型融合 模型融合:通过融合多个不同的模型,可能提升机器学习的性能.这一方法在各种机器学习比赛中广泛应用, 也是在比 ...

  10. 了解selenium这个工具

    selenium 也不是简单一个工具,而是由几个工具组成,每个工具都有其特点和应用场景.   selenium IDE selenium IDE 是嵌入到Firefox浏览器中的一个插件,实现简单的浏 ...