目录
3.3 消费-生产并存(consume-transform-produce)
4.2.2 tranaction id 、productid 和 epoch
4.4 Consume-transform-Produce 的流程
4.4.2 ControlMessage和Transaction markers
4.4.3 Transaction Coordinator 和 Transaction Log
4.5.3 Absorted Transaction Index
本文概览:在Kafka 0.11.0.0引入了EOS(exactly once semantics,精确一次处理语义)的特性,这个特性包括kafka幂等性和kafka事务两个属性。本小节对这个属性进行介绍。
1 生产者幂等性
1.1 引入
幂等性引入目的:
- 生产者重复生产消息。生产者进行retry会产生重试时,会重复产生消息。有了幂等性之后,在进行retry重试时,只会生成一个消息。
1.2 幂等性实现
1.2.1 PID 和 Sequence Number
为了实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number。
- PID。每个新的Producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
- Sequence Numbler。(对于每个PID,该Producer发送数据的每个<Topic, Partition>都对应一个从0开始单调递增的Sequence Number。
Broker端在缓存中保存了这seq number,对于接收的每条消息,如果其序号比Broker缓存中序号大于1则接受它,否则将其丢弃。这样就可以实现了消息重复提交了。但是,只能保证单个Producer对于同一个<Topic, Partition>的Exactly Once语义。不能保证同一个Producer一个topic不同的partion幂等。

实现幂等之后

1.2.2 生成PID的流程
在执行创建事务时,如下
| 1 |
Producer<String, String> producer = new KafkaProducer<String, String>(props); |
会创建一个Sender,并启动线程,执行如下run方法,在maybeWaitForProducerId()中生成一个producerId,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 |
==================================== 类名:Sender ====================================
void run(long now) { if (transactionManager != null) { try { ........ if (!transactionManager.isTransactional()) { // 为idempotent producer生成一个producer id maybeWaitForProducerId(); } else if (transactionManager.hasUnresolvedSequences() && !transactionManager.hasFatalError()) { ........ |
1.3 幂等性的应用实例
1、配置属性
需要设置:
- enable.idempotence,需要设置为ture,此时就会默认把acks设置为all,所以不需要再设置acks属性了。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
private Producer buildIdempotProducer(){
// create instance for properties to access producer configs Properties props = new Properties();
// bootstrap.servers是Kafka集群的IP地址。多个时,使用逗号隔开 props.put("bootstrap.servers", "localhost:9092");
props.put("enable.idempotence",true);
//If the request fails, the producer can automatically retry, props.put("retries", 3);
//Reduce the no of requests less than 0 props.put("linger.ms", 1);
//The buffer.memory controls the total amount of memory available to the producer for buffering. props.put("buffer.memory", 33554432);
// Kafka消息是以键值对的形式发送,需要设置key和value类型序列化器 props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<String, String>(props);
return producer; } |
2、发送消息
跟一般生成者一样,如下
| 1 2 3 4 5 6 7 |
public void produceIdempotMessage(String topic, String message) { // 创建Producer Producer producer = buildIdempotProducer(); // 发送消息 producer.send(new ProducerRecord<String, String>(topic, message)); producer.flush(); } |
此时,因为我们并没有配置transaction.id属性,所以不能使用事务相关API,如下
| 1 |
producer.initTransactions(); |
否则会出现如下错误:
Exception in thread “main” java.lang.IllegalStateException: Transactional method invoked on a non-transactional producer.
at org.apache.kafka.clients.producer.internals.TransactionManager.ensureTransactional(TransactionManager.java:777)
at org.apache.kafka.clients.producer.internals.TransactionManager.initializeTransactions(TransactionManager.java:202)
at org.apache.kafka.clients.producer.KafkaProducer.initTransactions(KafkaProducer.java:544)
2 事务属性
2.1 事务属性理解
事务属性是2017年Kafka 0.11.0.0引入的新特性。类似于数据库事务,只是这里的数据源是Kafka,kafka事务属性是指一系列的生产者生产消息和消费者提交偏移量的操作在一个事务,或者说是是一个原子操作),同时成功或者失败。
注意:在理解消息的事务时,一直处于一个错误理解就是如下代码中,把操作db的业务逻辑跟操作消息当成是一个事务。其实这个是有问题的,操作DB数据库的数据源是DB,消息数据源是kfaka,这是完全不同两个数据,一种数据源(如mysql,kafka)对应一个事务,所以它们是两个独立的事务:kafka事务指kafka一系列 生产、消费消息等操作组成一个原子操作;db事务是指操作数据库的一系列增删改操作组成一个原子操作。
| 1 2 3 4 5 6 7 8 |
void kakfa_in_tranction(){ // 1.kafa的操作:读取消息或者生产消息 kafkaOperation();
// 2.db操作 dbOperation()
} |
2.2 引入事务目的
在事务属性之前先引入了生产者幂等性,它的作用为:
- 生产者多次发送消息可以封装成一个原子操作,要么都成功,要么失败
- consumer-transform-producer模式下,因为消费者提交偏移量出现问题,导致在重复消费消息时,生产者重复生产消息。需要将这个模式下消费者提交偏移量操作和生成者一系列生成消息的操作封装成一个原子操作。
消费者提交偏移量导致重复消费消息的场景:消费者在消费消息完成提交便宜量o2之前挂掉了(假设它最近提交的偏移量是o1),此时执行再均衡时,其它消费者会重复消费消息(o1到o2之间的消息)。
2.3 事务操作的API
producer提供了initTransactions, beginTransaction, sendOffsets, commitTransaction, abortTransaction 五个事务方法。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
/** * 初始化事务。需要注意的有: * 1、前提 * 需要保证transation.id属性被配置。 * 2、这个方法执行逻辑是: * (1)Ensures any transactions initiated by previous instances of the producer with the same * transactional.id are completed. If the previous instance had failed with a transaction in * progress, it will be aborted. If the last transaction had begun completion, * but not yet finished, this method awaits its completion. * (2)Gets the internal producer id and epoch, used in all future transactional * messages issued by the producer. * */ public void initTransactions();
/** * 开启事务 */ public void beginTransaction() throws ProducerFencedException ;
/** * 为消费者提供的在事务内提交偏移量的操作 */ public void sendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata> offsets, String consumerGroupId) throws ProducerFencedException ;
/** * 提交事务 */ public void commitTransaction() throws ProducerFencedException;
/** * 放弃事务,类似回滚事务的操作 */ public void abortTransaction() throws ProducerFencedException ; |
3 事务属性的应用实例
在一个原子操作中,根据包含的操作类型,可以分为三种情况,前两种情况是事务引入的场景,最后一种情况没有使用价值。
只有Producer生产消息;
消费消息和生产消息并存,这个是事务场景中最常用的情况,就是我们常说的“consume-transform-produce ”模式
只有consumer消费消息,这种操作其实没有什么意义,跟使用手动提交效果一样,而且也不是事务属性引入的目的,所以一般不会使用这种情况
3.1 相关属性配置
使用kafka的事务api时的一些注意事项:
- 需要消费者的自动模式设置为false,并且不能子再手动的进行执行consumer#commitSync或者consumer#commitAsyc
- 生产者配置transaction.id属性
- 生产者不需要再配置enable.idempotence,因为如果配置了transaction.id,则此时enable.idempotence会被设置为true
- 消费者需要配置Isolation.level。在consume-trnasform-produce模式下使用事务时,必须设置为READ_COMMITTED。
3.2 只有写
创建一个事务,在这个事务操作中,只有生成消息操作。代码如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |

本文详细介绍了Kafka中的生产者幂等性和事务属性,包括PID和Sequence Number的实现、幂等性应用、事务操作API以及事务的最佳实践。重点阐述了幂等性和事务性之间的关系,强调了在Consume-transform-Produce模式下的事务处理流程,并讨论了消费读取事务消息的顺序。此外,还探讨了如何保证消息不丢、不重复生产和处理的策略。
最低0.47元/天 解锁文章
2530

被折叠的 条评论
为什么被折叠?



