springboot集成整合kafka-kafka事务、生产者事务支持、kafkaTemplate.executeInTransaction

写在前面:各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟。多谢!

先看下下面这种情况,程序都出错了,按理说消息也不应该成功

    @GetMapping("/send9")
    public void test9(String message) {
        kafkaTemplate.send(topic, message);
        throw new RuntimeException("fail");
    }

但是执行结果是发生了异常并且消息发送成功了:

springboot集成整合kafka-kafka事务、生产者事务支持、kafkaTemplate.executeInTransaction_第1张图片

Kafka 同数据库一样支持事务,当发生异常的时候可以进行回滚,确保消息监听器不会接收到一些错误的或者不需要的消息。kafka事务属性是指一系列的生产者生产消息和消费者提交偏移量的操作在一个事务,或者说是是一个原子操作),同时成功或者失败。使用事务也很简单,需要先开启事务支持,然后再使用。

先看下怎么开启事务,如果使用默认配置只需要在yml添加spring.kafka.producer.transaction-id-prefix配置来开启事务,之前没有使用默认的配置,自定义的kafkaTemplate,那么需要在ProducerFactory中设置事务Id前缀开启事务并将KafkaTransactionManager注入到spring中,看下KafkaProducerConfig完整代码:

@Configuration
@EnableKafka
public class KafkaProducerConfig {

    @Value("${kafka.producer.servers}")
    private String servers;
    @Value("${kafka.producer.retries}")
    private int retries;

    public Map producerConfigs(){
        Map props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
        props.put(ProducerConfig.RETRIES_CONFIG,retries);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        // 配置分区策略
        props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,"com.example.springbootkafka.config.CustomizePartitioner");
        // 配置生产者拦截器
        props.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,"com.example.springbootkafka.interceptor.CustomProducerInterceptor");
        // 配置拦截器消息处理类
        SendMessageInterceptorUtil sendMessageInterceptorUtil = new SendMessageInterceptorUtil();
        props.put("interceptorUtil",sendMessageInterceptorUtil);
        return props;
    }

    @Bean
    public ProducerFactory producerFactory(){
        DefaultKafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory(producerConfigs());
        //设置事务Id前缀 开启事务
        producerFactory.setTransactionIdPrefix("tx-");
        return producerFactory;
    }

    @Bean
    public KafkaTemplate kafkaTemplate(){
        return new KafkaTemplate<>(producerFactory());
    }

    @Bean
    public KafkaTransactionManager kafkaTransactionManager(ProducerFactory producerFactory) {
        return new KafkaTransactionManager(producerFactory);
    }
}

配置开启事务后,使用大体有两种方式,先记录下第一种使用事务方式:使用 executeInTransaction 方法

直接看下代码:

    @GetMapping("/send11")
    public void test11(String message) {
        kafkaTemplate.executeInTransaction(operations ->{
            operations.send(topic,message);
            throw new RuntimeException("fail");
        });
    }

当然你可以这么写:

    @GetMapping("/send11")
    public void test11(String message) {
        kafkaTemplate.executeInTransaction(new KafkaOperations.OperationsCallback(){
            @Override
            public Object doInOperations(KafkaOperations operations) {
                operations.send(topic,message);
                throw new RuntimeException("fail");
            }
        });
    }

启动项目,访问http://localhost:8080/send10?message=test10 结果如下: 

springboot集成整合kafka-kafka事务、生产者事务支持、kafkaTemplate.executeInTransaction_第2张图片

springboot集成整合kafka-kafka事务、生产者事务支持、kafkaTemplate.executeInTransaction_第3张图片

如上:消费者没打印消息,说明消息没发送成功,并且前面会报错org.apache.kafka.common.KafkaException: Failing batch since transaction was aborted 的错误,说明事务生效了。

第一种使用事务方式:使用 @Transactional 注解方式  直接在方法上加上@Transactional注解即可,看下代码:

    @GetMapping("/send12")
    @Transactional
    public void test12(String message) {
        kafkaTemplate.send(topic, message);
        throw new RuntimeException("fail");
    }

如果开启的事务,则后续发送消息必须使用@Transactional注解或者使用kafkaTemplate.executeInTransaction() ,否则抛出异常,异常信息如下:

贴下完整的异常吧:java.lang.IllegalStateException: No transaction is in process; possible solutions: run the template operation within the scope of a template.executeInTransaction() operation, start a transaction with @Transactional before invoking the template method, run in a transaction started by a listener container when consuming a record

你可能感兴趣的:(kafka)