Kafka生产者事务和幂等

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

目录

1 生产者幂等性

1.1 引入

1.2 幂等性实现

1.2.1 PID 和 Sequence Number

1.2.2 生成PID的流程

1.3 幂等性的应用实例

2 事务属性

2.1 事务属性理解

2.2 引入事务目的

2.3 事务操作的API

3 事务属性的应用实例

3.1 相关属性配置

3.2 只有写

3.3 消费-生产并存(consume-transform-produce)

3.4 只有读

4 生产者事务的实现

4.1 相关配置

4.1.1 Broker configs

4.1.2 Producer configs

4.1.3 Consumer configs

4.2  幂等性和事务性的关系

4.2.1 两者关系

4.2.2 tranaction id 、productid 和 epoch

4.3 事务最佳实践-单实例的事务性

4.4  Consume-transform-Produce 的流程

4.4.1 文件类型和查看命令

4.4.2 ControlMessage和Transaction markers

4.4.3 Transaction Coordinator 和 Transaction Log

4.5 消费读取事务消息(READ_COMMITED)

4.5.1 老版本-读取事务消息顺序

4.5.2 新版本-读取事务消息顺序

4.5.3 Absorted Transaction Index

4.5.3  问题

5 其他思考


本文概览:在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

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提供了两个关键的特性来确保消息的一致性可靠性:等性事务性。 1. 等性(Idempotent):Kafka生产者可以配置为生产者,即保证在发送消息时不会产生重复消息。等性意味着无论发送多少次相同的消息,最终结果都是一样的,不会导致副作用。等性生产者通过在消息中添加序列号来实现,Kafka在接收到重复消息时会自动去重,确保只有一条消息被写入。 2. 事务性(Transactional):Kafka从0.11版本开始引入了事务性支持。事务性消费者可以以事务的方式读取处理消息,同时也支持事务生产者在写入消息时保持原子性。事务性消费者可以确保读取的消息在被处理后不会被重复消费,并且在处理失败时可以回滚事务事务生产者可以将多个写操作组合为一个原子事务,要么全部成功提交,要么全部回滚。 使用等性事务性可以帮助确保在Kafka中进行消息的可靠处理传递。等性消费者事务性消费者可以避免重复消费数据不一致的问题,而等性生产者事务生产者可以确保消息的原子性写入可靠提交。 需要注意的是,启用事务等性特性会增加一定的性能开销,因此在使用时需要权衡性能一致性的需求,并根据实际情况进行配置调整。同时,事务等性特性也需要结合Kafka的相应API配置进行正确的使用管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值