目录
概述
kafka 从0.11版本开始支持exactly-once语义。从此,流式处理框架数据准确性语义at-most-once、at-least-once、exactly-once全部支持。exactly-once语义的支持复杂度是最高的,单纯从语义角度上可理解为exactly-once=at-least-once+幂等,kafka事务的引入实现exactly-once语义。kafka事务对ACID做了非完全性的支持,不支持事务回滚,事务隔离级别针对Consumer对数据的可见性提出LSO(last-stable offset)概念,Broker端对同一个txnid的事务做串行处理。本文从Consumer、Producer、Broker三个角色分析kafka事务。(全文基于2.3版本)
幂等
kafka中幂等这个概念是针对Proudcer--->Broker写场景讲的,基于at-least-once语义一条消息Broker只能成功存储一次。kafka引入了pid+sequence number实现了topic-partition局性幂等性。pid全局唯一,使用curator框架由zk生成(curator生成全局ID方案),sequence number由Producer累加生成,Broker侧会缓存5条最近pid+sequence组合标识做幂等判断。
配置:enable.idempotence=true,会自动开启幂等,默认为false。(注意:如果设置为true,以下几个配置必须设置正确,否则会抛出ConfigException)
max.in.flight.requests.per.connection | 小于等于5 |
retries | 大于0 |
acks | 必须设置为all |
如果Producer僵死或者Producer服务重启将会打破幂性,使程序失效。解决跨会话、跨topic-partiton写幂等需要结合kafka提供的事务
事务
事务ACID四个特性,kafka做了非传统意义上的支持。
原子性:跨topic-partition或Producer会话原子性写入。事务不支持回滚,但失败的事务数据会打上abort标识,也确保了要么成功,要么失败的语义
一致性:通过LSO水位,确保已commit事务产生的数据对Consumer可见
隔离性:概念很弱化,结合LSO支持Consumer支持read_committed和read_uncomitted
持久性:采用多副本策略,支持高可用。
分布式事务有2PC、3PC、ZAB、Paxos等协议,kafka采用了类似2PC协议,引入了事务协调者TransactionCoordinator概念。
2PC协议:
角色:
1. 协调者(Coordinator)或者叫TM(TransactionManagger)
kafka事务协调者为TransactionsCoordinator,负责事务状态管理、Transaction Marker
2. 事务参与者(participants)
kafka事务参与者为Producer、Broker,但kafka支持consumer-transform-producer模式,因此还有Consumer。如果用RM(ResourceManager)概念只能表达Broker。
Producer:Consumer:Broker为1:1:N
kafka分布式事务并没有2PC协议的TM单点,Participants僵死的问题,TransactionCoordinator支持高可用,Producer支持跨会话。
代码示例
代码示例中把事务处理流程(DataFlow)和Producer关键的配置项均做了说明。具体的流程图可见Exactly Once Delivery and Transactional Messaging
public static void sendWithTransaction(String topic, Consumer codeBlock){
//下面列出些核心的配置
Properties pro = new Properties();
pro.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"host:port");
pro.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
pro.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
//Leader/Follower都存储成功后返回
pro.put(ProducerConfig.ACKS_CONFIG,"all");
//单个Producer实例可使用的总内存,默认为32M,内存会循环使用
pro.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 32 * 1024 * 1024L);
//2.3版本,当设置事务时必须小于等于5
pro.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 1);
//2.3版本,当设置事务时必须大于1
pro.put(ProducerConfig.RETRIES_CONFIG, 1);
//开启幂等
pro.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");
//开启事务,每个事务ID与Producer 的PID是一一对应的,出现重复会抛异常
pro.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "TXNID_"+Thread.currentThread().getId());
//事务处理时间,必须小于transaction.max.timeout.ms,默认为60秒
pro.put(ProducerConfig.TRANSACTION_TIMEOUT_CONFIG, 10000L);
//默认为16KB,每个Partition独立缓存大小为16KB,消息达到16KB后发出
pro.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384L);
//默认为0,配合batch_size使用,5毫秒后消息内容没有16KB,也发发出
pro.put(ProducerConfig.LINGER_MS_CONFIG, 5);
//初始化kafkaProducer对象,kafkaProduer是线程安全的,一般采用单例模式
KafkaProducer producer = new KafkaProducer(pro);
try{
/**
* 实始化事务准备阶段的信息。如:
* 1、寻找TransactionCoordinator
* 2、申请PID或者PID+epoch
* 3、根据txid寻找未处理结束(指未Commit或者Abort)的事务
* 4、检查txid与PID一一对应关系
*/
producer.initTransactions();
//开始事务(Producer状态转换为in_transaction),不会与TransactionCoordinator交互
producer.beginTransaction();
/**
* 发送消息
* 1、发送AddPartitionsToTxnRequest,Producer ---> TransactionCoordinator,持久化到txn_log中
* 2、发送ProducerRequest,Producer ---> Broker(涉及多个Borker),持久化到user_topic
*/
producer.send(new ProducerRecord(topic, "key", "value"));
producer.send(new ProducerRecord(topic, "key", "value"));
/**
* consumer-transform-produce模式中使用,发送consumer消费的offset
* 1、发送AddOffsetCommitToTxnRequest,Producer ---> TransactionCoordinator,持久化到txn_log中
* 2、发送TxnOffsetCommitRequest,Producer ---> GroupCoordinator,持久化到_consumer-offsets topic中
*/
producer.sendOffsetsToTransaction(null, "consumerGroup");
/**
* 提交事务
* 发送EndTxnRequest,Producer--->TransactionCoordinator--->Borker(涉及多个Borker)
* 1、写入一个PREPARE_COMMIT或者PREPARE_ABORT消息到txn_log中
* 2、发送WriteTxnMarkerRequest,TransactionCoordinator--->Broker(涉及多个Borker)
* 3、TransactionCoordinator写入Commit或者Abort到 txn_log中
*/
producer.commitTransaction();
}catch (Exception e){
//终止事务
producer.abortTransaction();
}finally {
//关闭Producer,不关闭会导致连接泄露
producer.close();
}
}
来源:CSDN
作者:IWBS
链接:https://blog.csdn.net/asd491310/article/details/103432495