【发布时间】:2019-06-07 00:59:14
【问题描述】:
考虑这个 Kafka 消费者,它从主题接收数据,将其缓冲到 PreparedStatement 中,当批处理 10 万条记录时,它会向数据库发出 INSERT 查询。
这在数据仍然传入之前运行良好。但是,例如,当缓冲 20K 记录并且没有更多记录传入时,它仍会等待更多 80K 记录,直到在 flushes 语句中。但是如果在一段时间后停止,我想刷新那些 20K。我怎样才能做到这一点?我看不出有什么方法可以抓住它。
例如,在使用基于 librdkafka 的 php-rdkafka 扩展的 PHP 中,当达到分区末尾时,我会得到 RD_KAFKA_RESP_ERR__PARTITION_EOF,因此在发生这种情况时很容易挂钩缓冲区刷新。
我尝试简化代码,只保留重要部分
public class TestConsumer {
private final Connection connection;
private final CountDownLatch shutdownLatch;
private final KafkaConsumer<String, Message> consumer;
private int processedCount = 0;
public TestConsumer(Connection connection) {
this.connection = connection;
this.consumer = new KafkaConsumer<>(getConfig(), new StringDeserializer(), new ProtoDeserializer<>(Message.parser()));
this.shutdownLatch = new CountDownLatch(1);
}
public void execute() {
PreparedStatement statement;
try {
statement = getPreparedStatement();
} catch (SQLException e) {
throw new RuntimeException(e);
}
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
commit(statement);
consumer.wakeup();
}));
consumer.subscribe(Collections.singletonList("source.topic"));
try {
while (true) {
ConsumerRecords<String, Message> records = consumer.poll(Duration.ofMillis(Long.MAX_VALUE));
records.forEach(record -> {
Message message = record.value();
try {
fillBatch(statement, message);
statement.addBatch();
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
processedCount += records.count();
if (processedCount > 100000) {
commit(statement);
}
}
} catch (WakeupException e) {
// ignore, we're closing
} finally {
consumer.close();
shutdownLatch.countDown();
}
}
private void commit(PreparedStatement statement) {
try {
statement.executeBatch();
consumer.commitSync();
processedCount = 0;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
protected void fillBatch(PreparedStatement statement, Message message) throws SQLException {
try {
statement.setTimestamp(1, new Timestamp(message.getTime() * 1000L));
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
【问题讨论】:
-
为什么不通过记住开始时间来添加手动超时,然后在每次迭代后检查,即
if (Duration.between(startTime, LocalDateTime.now()).toMillis() > timeoutMs) { commit(statement); break; } -
正如@daniu 提到的,您可以添加一个超时,这样每当达到计数或发生超时时,您就可以执行该语句。是你可以在骆驼等许多集成框架中找到的东西
-
感谢 cmets!所以这里的正确方法是手动计算持续时间并调整
poll()的持续时间,使其不会永远阻塞。
标签: java apache-kafka kafka-consumer-api