用户提供一个事务id,用于初始化生产者。
一、额外的概念
- Transaction Coordinator
类似于consumer group coordinator,管理事务日志和生产者的pid。 - Transaction Log
类似于 Consumer Offsets topic,事务日志是事务的持久化存储,存储着每个活动的事务状态。已经完成的事务,其实事务日志就没有意义了。 - Control Messages.
是写到用户topic的特殊的消息,这些日志不会暴露给用户 。broker用来提示消费者之前收到的事务消息是commited还是aborted的。 - TransactionalId
每个生产者有唯一的TransactionalId,当多个生产者有相同的TransactionalId时,会使用最新的那个生产者。
KafkaProducer producer = createKafkaProducer(“bootstrap.servers”, “localhost:9092”,“transactional.id”, “my-transactional-id”);
- producer epoch
保证一个TransactionalId逻辑上只对应一个生产者。可以解决僵尸实例,即一个生产者挂掉后,系统重新启动一个新的生产者。但是原来的生产者又重新启动了,这时kafka就会把这生产者停掉。
二、 Data Flow
网上很多地方可以找到这个图片。
这张图描述了一个事务的完整流程:
6. Finding a transaction coordinator – the FindCoordinatorRequest
寻找Transaction Coordinator。
7. Getting a producer Id – the InitPidRequest
获取分配的producer Id(pid)(需要显示指定TransactionalId)。
8. Starting a Transaction – The beginTransaction() API
这里只是从producer的角度表示开启了一个新事务,从Transaction Coordinator的角度,并没有开始一个事务。
9. The consume-transform-produce loop
这是一个比较长的过程,有很多请求在这个过程里。
4.1 AddPartitionsToTxnRequest
当producer写内容到一个新分区TopicPartition的时候,向Transaction Coordinator发消息,Transaction Coordinator记录到事务日志,表示一个事务的开始。之所以要记录producer事务里写的每一个分区是因为事务结束后要发送commit or abort markers到每个分区。
4.2 ProduceRequest
4.3 AddOffsetCommitsToTxnRequest
4.4 TxnOffsetCommitRequest
- Committing or Aborting a Transaction
准备提交或者终止事务,当producer发送请求之后,事务协调器做以下事情:- 写PREPARE_COMMIT or PREPARE_ABORT 消息到事务日志。
- 写COMMIT (or ABORT) markers 到每个分区。
- Finally writes the COMMITTED (or ABORTED) message to transaction log.
当消费者接收到事务消息后,会缓存下来,一直等到接收到COMMIT(PID) or ABORT消息,才决定怎么处理消息。
三、 异常情况
官方文档
- 客户端或者服务端在事务的过程中可能发生错误,并且有可能已经往部分分区写了消息。这时一般有两种解决办法,1. 事务开始后,broker会启动timer,当timer超时后,终止事务。2. Transaction Coordinator主动终止事务。也就是往已经发送的分区,再发送终止消息。然后再决定是否由其他的生产者重启事务。
四、 消费者
消费者有两种模式:READ_COMMITTED or READ_UNCOMMITTED
READ_UNCOMMITTED:做了唯一改变就是,直接忽略 transactional control messages。
READ_COMMMITTED: 消费者客户端有个map [TxId-Partition] -> messages的映射。把收到的消息先存储起来,直到收到了 COMMIT or ABORT的事务控制消息。