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. java-导入import

    1 package face_package; 2 3 import face_packagedemoA.DemoA; 4 import face_packagedemoB.DemoB; 5 //真正 ...

  2. /etc/passwd详解

    root:x:0:0:root:/root:/bin/bash  bin:x:1:1:bin:/bin:/sbin/nologin  daemon:x:2:2:daemon:/sbin:/sbin/n ...

  3. Java多线程专题1: 并发与并行的基础概念

    合集目录 Java多线程专题1: 并发与并行的基础概念 什么是多线程并发和并行? 并发: Concurrency 特指单核可以处理多任务, 这种机制主要实现于操作系统层面, 用于充分利用单CPU的性能 ...

  4. Lesson4——NumPy 数组属性

    NumPy 教程目录 NumPy 数组的维数称为秩(rank),秩就是轴的数量,即数组的维度,一维数组的秩为 1,二维数组的秩为 2,以此类推. 在 NumPy中,每一个线性的数组称为是一个轴(axi ...

  5. json中传递数组和list

    json的数据类型:List,数组,数字,字符串,逻辑值,对象,null 1.如果json传递的是数组,格式: { "name":"网站", "num ...

  6. Hadoop启动错误——ERROR: Attempting to operate on hdfs namenode as root but there is no HDFS_NAMENODE_USER defined. Aborting operation.

    错误如下所示; [root@localhost sbin]# start-all.sh Starting namenodes on [192.168.71.129] ERROR: Attempting ...

  7. Linux基础:操作系统的启动

    Centos6: # 1.加电自检(BIOS)# 2.MBR引导(512k)dd </dev/zero >/dev/sda bs=1k count=400 # 3.GRUB菜单(选择系统) ...

  8. Diary / Solution Set -「WC 2022」线上冬眠做噩梦

      大概只有比较有意思又不过分超出能力范围的题叭.   可是兔子的"能力范围" \(=\varnothing\) qwq. 「CF 1267G」Game Relics   任意一个 ...

  9. Solution -「LOJ #6053」简单的函数

    \(\mathcal{Description}\)   Link.   积性函数 \(f\) 满足 \(f(p^c)=p\oplus c~(p\in\mathbb P,c\in\mathbb N_+) ...

  10. LibOpenCM3(三) .ld文件(连接器脚本)和startup代码说明

    目录 LibOpenCM3(一) Linux下命令行开发环境配置 LibOpenCM3(二) 项目模板 Makefile分析 LibOpenCM3(三) .ld文件(连接器脚本)和startup代码说 ...