【发布时间】:2017-12-14 15:35:41
【问题描述】:
我正在 Spring Boot 下创建一个 Kafka Spring 生产者,它将数据发送到 Kafka,然后写入数据库;我希望所有这些工作都在一个事务中。我是 Kafka 的新手,也不是 Spring 方面的专家,并且遇到了一些困难。任何指针都非常感谢。
到目前为止,我的代码已成功循环写入 Kafka。我还没有设置 数据库,但已通过在配置中向 producerFactory 添加 transactionIdPrefix 来设置全局事务:
producerFactory.setTransactionIdPrefix("MY_SERVER");
并将@Transactional 添加到 Kafka 发送的方法中。最终我打算用同样的方法来做我的数据库工作。
问题:代码第一次运行良好。但是,如果我停止程序,即使是干净地,我发现代码在我第二次运行它时会挂起,因为它进入 @Transactional 方法。如果我注释掉@Transactional,它会进入方法但挂在kafa模板send()上。
问题似乎出在交易 ID 上。如果我更改前缀并重新运行,程序第一次运行良好,但再次运行时挂起,直到选择新前缀。由于重启后 trans ID 计数器从零开始,如果 trans ID 前缀没有改变,那么重启时将使用相同的 trans ID。
在我看来,原来的 transID 仍然在服务器上打开,并且从未提交过。 (我可以使用控制台消费者从主题中读取数据,但这将读取未提交)。但如果是这样的话,我如何让 spring 提交 trans?我想我的配置一定是错误的。或者——这个问题是否可能是反式 ID 永远无法重复使用? (在这种情况下,如何解决?)
这是我的相关代码。配置是:
@SpringBootApplication
public class MYApplication {
@Autowired
private static ChangeSweeper changeSweeper;
@Value("${kafka.bootstrap-servers}")
private String bootstrapServers;
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
DefaultKafkaProducerFactory<String, String> producerFactory=new DefaultKafkaProducerFactory<>(configProps);
producerFactory.setTransactionIdPrefix("MY_SERVER");
return producerFactory;
}
@Bean
public KafkaTransactionManager<String, String> KafkaTransactionManager() {
return new KafkaTransactionManager<String, String>((producerFactory()));
}
@Bean(name="kafkaProducerTemplate")
public KafkaTemplate<String, String> kafkaProducerTemplate() {
return new KafkaTemplate<>(producerFactory());
}
而进行交易的方法是:
@Transactional
public void send( final List<Record> records) {
logger.debug("sending {} records; batchSize={}; topic={}", records.size(),batchSize, kafkaTopic);
// Divide the record set into batches of size batchSize and send each batch with a kafka transaction:
for (int batchStartIndex = 0; batchStartIndex < records.size(); batchStartIndex += batchSize ) {
int batchEndIndex=Math.min(records.size()-1, batchStartIndex+batchSize-1);
List<Record> nextBatch = records.subList(batchStartIndex, batchEndIndex);
logger.debug("## batch is from " + batchStartIndex + " to " + batchEndIndex);
for (Record record : nextBatch) {
kafkaProducerTemplate.send( kafkaTopic, record.getKey().toString(), record.getData().toString());
logger.debug("Sending> " + record);
}
// I will put the DB writes here
}
【问题讨论】:
-
>
is the issue possibly that trans ID's can never be reused- 应该不是问题,因为我们调用initTransactions()- 它的 javadocs 说If the previous instance had failed with a transaction in progress, it will be aborted.- 我会看看我是否可以重现您的问题。 -
您的集群中是否有三个代理(默认要求)?我在
initTransactions()上挂断了,只有一个代理,在服务器日志上,我看到Number of alive brokers '1' does not meet the required replication factor '3' for the transactions state topic (configured via 'transaction.state.log.replication.factor')- 但是你的第一次发送不起作用。 -
我只有一个经纪人——只是在本地开发。正如您所指出的,发送工作正常,一个接一个,一批接一批,直到我终止程序并重新启动。所以发送似乎没问题,但交易似乎是问题所在。请注意,我现在在 Wndows 上运行,我注意到了其他问题。
-
看我的回答;另请参阅this github issue,其中记者引用了apache/kafka#3283关于Windoze的一些问题。
标签: spring apache-kafka spring-transactions spring-kafka