Java 如何将@Transactional与@KafkaListener一起使用?
是否有可能将声明性tx管理(通过@Transactional)与@KafkaListener注释方法一起使用? 例如,我想使用它来定义每个侦听器的单独tx超时。 我的设置如下: TransactionManager:Java 如何将@Transactional与@KafkaListener一起使用?,java,spring-boot,spring-kafka,spring-transactions,Java,Spring Boot,Spring Kafka,Spring Transactions,是否有可能将声明性tx管理(通过@Transactional)与@KafkaListener注释方法一起使用? 例如,我想使用它来定义每个侦听器的单独tx超时。 我的设置如下: TransactionManager: @Bean @ConditionalOnBean(value = {HibernateTransactionManager.class}) public ChainedKafkaTransactionManager<Object, Object
@Bean
@ConditionalOnBean(value = {HibernateTransactionManager.class})
public ChainedKafkaTransactionManager<Object, Object> chainedHibernateTm(KafkaTransactionManager<String, String> kafkaTransactionManager,
org.springframework.orm.hibernate5.HibernateTransactionManager hibernateTransactionManager) {
return new ChainedKafkaTransactionManager<>(kafkaTransactionManager, hibernateTransactionManager);
}
问题是-KafkCamessageListenerContainer在调用该方法之前创建自己的事务-它使用自己的事务模板:
@Nullable
private TransactionTemplate determineTransactionTemplate() {
return this.transactionManager != null
? new TransactionTemplate(this.transactionManager)
: null;
}
未使用TransactionInterceptor。那么,如何为具体的@KafkaListener方法设置特定的发送超时呢?通过普通的
@Transactional
进行事务管理的各个方面不会像您所希望的那样与Kafka侦听器一起工作-没有内置的交互
有一章专门介绍如何在事务中使用应用程序事件:使用注释有助于确保事务已成功完成
您可以使用@EventListener注释注册常规事件侦听器。如果需要将其绑定到事务,请使用@TransactionalEventListener。当您这样做时,默认情况下侦听器被绑定到事务的提交阶段
可以这样做,但有点复杂,因为您必须将消耗的偏移量发送到Kafka事务 您可以对容器使用
kafkartransactionmanager
,对hibernatransactionmanager
使用@Transactional
,而不是使用chainedkafkaftransactionmanager
这将给出类似的结果,因为hibernate TX将在Kafka事务之前提交(如果hibernate提交失败,Kafka TX将回滚)
编辑
要在每个侦听器容器中配置不同的chained TM,可以执行以下操作
@组成部分
类ContainerFactoryCustomizer{
ContainerFactoryCustomizer(AbstractKafkaListenerContainerFactory<?, ?, ?> factory,
ChainedKafkaTransactionManager<?, ?> chainedOne,
ChainedKafkaTransactionManager<?, ?> chainedTwo) {
factory.setContainerCustomizer(
container -> {
String groupId = container.getContainerProperties().getGroupId();
if (groupId.equals("foo")) {
container.getContainerProperties().setTransactionManager(chainedOne);
}
else {
container.getContainerProperties().setTransactionManager(chainedTwo);
}
});
}
Garry Russell。谢谢。但是,您能给我一些更详细的建议,如何实现每个KafkaListener的自定义发送超时吗?我仍然希望与hibernate发送管理器同步。您说过这是可以做到的,但我不知道如何做到。虽然hibernate有事务超时的概念,但Kafka事务中没有超时的概念最简单的解决方案是让容器只管理Kafka事务,并对Hibernate TM使用
@Transactional
;结果将类似于事务同步。要在容器中使用链式TM,可以将每个容器配置为使用不同的链式TM,其中Hibernate TM配置为different默认超时。请参阅对我的答案的编辑。感谢详细解释。如果我选择第一个选项(不带chained tm),我是否必须手动将消耗的偏移量发送到kafka事务?此选项看起来很有趣,因为我可以使用@Transactional当前(带chained tm)配置hib.tm此注释被完全忽略。我也考虑过定制KafkaListenerContainerManager,但我必须完全重写它,因为用于启动事务的TransactionTemplate是最终的,无法修改:)>如果我选择第一个选项(不带chained tm)我是否必须手动将消耗的偏移量发送到kafka事务?
。否;容器不知道DB tx,并将发送偏移量(如果DB tx提交)。如果DB提交失败,kafka tx将回滚(回滚后处理器将重新查找分区)。注释不会“完全忽略”,默认传播是REQUIRED
,拦截器看到存在一个现有事务,因此无需启动另一个事务。只有在容器不是事务性的情况下才需要自己发送偏移量。正确;但对于仅生产者事务,只使用事务同步更简单。
ContainerFactoryCustomizer(AbstractKafkaListenerContainerFactory<?, ?, ?> factory,
ChainedKafkaTransactionManager<?, ?> chainedOne,
ChainedKafkaTransactionManager<?, ?> chainedTwo) {
factory.setContainerCustomizer(
container -> {
String groupId = container.getContainerProperties().getGroupId();
if (groupId.equals("foo")) {
container.getContainerProperties().setTransactionManager(chainedOne);
}
else {
container.getContainerProperties().setTransactionManager(chainedTwo);
}
});
}
Where each chained TM has a Hibernate TM with a different default timeout.
The `groupid` is populated from the `@KafkaListener` `id` or `groupId` property.