我使用的是 Spring Boot 2.5.2 和 Spring Cloud 2020.0.3。我正在尝试包装一个休息服务调用,该调用使用 JPA(CrudRepository.save)将记录保存到数据库,然后使用 StreamBridge 使用 spring-cloud-stream(kafka binder)将消息发布到 Kafka 主题。我已经尝试了几件事,但似乎没有什么工作完全正确。我故意导致 JPA 问题(插入会违反唯一键约束的行),但 Kafka 消息似乎仍然发送给代理。
配置这种类型的流以使写入数据库和代理都是原子的的正确方法是什么?
HTTP -> JPA 保存 -> Kafka 发送
答案 0 :(得分:2)
您不需要事务管理器,但需要生产者工厂的 transactional.id。
如果发送是在 JPA 事务范围内执行的(例如使用 JPA TM 的 @Transactional
方法),kafka 模板会将 Kafka 事务与现有事务同步并提交或回滚取决于主交易。
您是否知道,即使是回滚记录,实际上也写入了日志?您必须将使用者属性 isolation.level
设置为 read_committed
才能不接收回滚记录;它默认为 read_uncommitted
。
编辑
有一个 bug 将仅生产者交易同步到现有交易;发送是在本地事务中执行的。
您可以使用 TransactionTemplate
启动 Kafka 事务作为解决方法:
@SpringBootApplication
public class So68460690Application {
public static void main(String[] args) {
SpringApplication.run(So68460690Application.class, args);
}
@Bean
public ApplicationRunner runner(StreamBridge bridge, Foo foo, KafkaTransactionManager<byte[], byte[]> ktm) {
return args -> {
new TransactionTemplate(ktm).executeWithoutResult(
status -> foo.doInTx(bridge)); // or execute() to return a result
};
}
@Bean
KafkaTransactionManager<byte[], byte[]> binderTM(BinderFactory bf) {
return new KafkaTransactionManager<>(((KafkaMessageChannelBinder) bf.getBinder("kafka", MessageChannel.class))
.getTransactionalProducerFactory());
}
}
@Component
class Foo {
@Transactional
public void doInTx(StreamBridge bridge) {
bridge.send("ouutput", "test");
throw new RuntimeException("testEx");
}
}
spring.cloud.stream.bindings.output.destination=so68460690
spring.cloud.stream.kafka.binder.transaction.transaction-id-prefix=tx.
spring.cloud.stream.kafka.binder.configuration.acks=all
logging.level.org.springframework.kafka=trace
2021-07-27 17:31:37.923 DEBUG 55933 --- [ main] o.s.k.core.DefaultKafkaProducerFactory : CloseSafeProducer [delegate=org.apache.kafka.clients.producer.KafkaProducer@56c8e6f0] beginTransaction()
2021-07-27 17:31:37.924 DEBUG 55933 --- [ main] o.s.k.t.KafkaTransactionManager : Created Kafka transaction on producer [CloseSafeProducer [delegate=org.apache.kafka.clients.producer.KafkaProducer@56c8e6f0]]
2021-07-27 17:31:37.927 DEBUG 55933 --- [ main] o.s.k.t.KafkaTransactionManager : Initiating transaction rollback
2021-07-27 17:31:37.928 DEBUG 55933 --- [ main] o.s.k.core.DefaultKafkaProducerFactory : CloseSafeProducer [delegate=org.apache.kafka.clients.producer.KafkaProducer@56c8e6f0] abortTransaction()