Spring Kafka异步发送调用块

Spring Kafka异步发送调用块,spring,asynchronous,apache-kafka,send,producer,Spring,Asynchronous,Apache Kafka,Send,Producer,我使用的是SpringKafka版本1.2.1,当Kafka服务器关闭/无法访问时,异步发送调用会阻塞一段时间。这似乎是TCP超时。代码如下所示: ListenableFuture<SendResult<K, V>> future = kafkaTemplate.send(topic, key, message); future.addCallback(new ListenableFutureCallback<SendResult<K, V>>()

我使用的是SpringKafka版本1.2.1,当Kafka服务器关闭/无法访问时,异步发送调用会阻塞一段时间。这似乎是TCP超时。代码如下所示:

ListenableFuture<SendResult<K, V>> future = kafkaTemplate.send(topic, key, message);
future.addCallback(new ListenableFutureCallback<SendResult<K, V>>() {
    @Override
    public void onSuccess(SendResult<K, V> result) {
        ...
    }

    @Override
    public void onFailure(Throwable ex) {
        ...
    }
});
ListenableFuture=kafkaTemplate.send(主题、键、消息);
future.addCallback(新ListenableFutureCallback(){
@凌驾
成功时公共无效(SendResult结果){
...
}
@凌驾
失效时的公共无效(可丢弃的ex){
...
}
});
我快速浏览了Spring Kafka代码,它似乎只是将任务传递给Kafka客户端库,将回调交互转换为未来的对象交互。查看kafka客户端库,代码变得更加复杂,我没有花时间完全理解它,但我猜它可能在同一线程中进行远程调用(至少是元数据)

作为一个用户,我期望返回未来的Spring Kafka方法立即返回,即使远程Kafka服务器无法访问

如果我的理解是错误的,或者这是一个错误,任何确认都是受欢迎的。现在,我把它变成了异步的

另一个问题是,SpringKafka文档一开始说,它提供了同步和异步发送方法。我找不到任何不返回期货的方法,可能文档需要更新


如果需要,我很乐意提供更多细节。谢谢。

我只是想确定一下。您是否应用了@EnableAsync注释?我想说的是,这可能是指定未来行为的关键

除了配置类上的@enablesync注释外,还需要在调用此代码的方法上使用@Async注释

这里有一些代码片段。卡夫卡制作人配置:

@EnableAsync
@Configuration
public class KafkaProducerConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(KafkaProducerConfig.class);

    @Value("${kafka.brokers}")
    private String servers;

    @Bean
    public Map<String, Object> producerConfigs() {
        Map<String, Object> props = new HashMap<>();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, servers);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        return props;
    }

    @Bean
    public ProducerFactory<String, GenericMessage> producerFactory(ObjectMapper objectMapper) {
        return new DefaultKafkaProducerFactory<>(producerConfigs(), new StringSerializer(), new JsonSerializer(objectMapper));
    }

    @Bean
    public KafkaTemplate<String, GenericMessage> kafkaTemplate(ObjectMapper objectMapper) {
        return new KafkaTemplate<String, GenericMessage>(producerFactory(objectMapper));
    }

    @Bean
    public Producer producer() {
        return new Producer();
    }
}
@EnableAsync
@配置
公共类KafkaProducerConfig{
私有静态最终记录器Logger=LoggerFactory.getLogger(KafkaProducerConfig.class);
@值(${kafka.brokers}”)
私有字符串服务器;
@豆子
公共地图产品配置(){
Map props=newhashmap();
put(ProducerConfig.BOOTSTRAP\u SERVERS\u CONFIG,SERVERS);
put(ProducerConfig.KEY\u SERIALIZER\u CLASS\u CONFIG,StringSerializer.CLASS);
put(ProducerConfig.VALUE\u SERIALIZER\u CLASS\u CONFIG,jsondesializer.CLASS);
返回道具;
}
@豆子
公共生产工厂生产工厂(ObjectMapper ObjectMapper){
返回新的DefaultKafkaProducerFactory(producerConfigs()、新的StringSerializer()、新的JsonSerializer(objectMapper));
}
@豆子
公共KafkaTemplate KafkaTemplate(ObjectMapper ObjectMapper){
返回新的KafkaTemplate(producerFactory(objectMapper));
}
@豆子
公共制片人{
返回新的生产者();
}
}
以及制作人本身:

public class Producer {

    public static final Logger LOGGER = LoggerFactory.getLogger(Producer.class);

    @Autowired
    private KafkaTemplate<String, GenericMessage> kafkaTemplate;

    @Async
    public void send(String topic, GenericMessage message) {
        ListenableFuture<SendResult<String, GenericMessage>> future = kafkaTemplate.send(topic, message);
        future.addCallback(new ListenableFutureCallback<SendResult<String, GenericMessage>>() {

            @Override
            public void onSuccess(final SendResult<String, GenericMessage> message) {
                LOGGER.info("sent message= " + message + " with offset= " + message.getRecordMetadata().offset());
            }

            @Override
            public void onFailure(final Throwable throwable) {
                LOGGER.error("unable to send message= " + message, throwable);
            }
        });
    }
}
公共类制作人{
公共静态最终记录器Logger=LoggerFactory.getLogger(Producer.class);
@自动连线
私人卡夫卡模板卡夫卡模板;
@异步的
公共void发送(字符串主题,GenericMessage消息){
ListenableFuture=kafkaTemplate.send(主题、消息);
future.addCallback(新ListenableFutureCallback(){
@凌驾
成功时公共无效(最终SendResult消息){
LOGGER.info(“sent message=“+message+”with offset=“+message.getRecordMetadata().offset());
}
@凌驾
失败时公共无效(最终可丢弃){
LOGGER.错误(“无法发送消息=“+消息,可丢弃”);
}
});
}
}

最佳解决方案是在生产者级别添加“回调”侦听器

@Bean
public KafkaTemplate<String, WebUserOperation> operationKafkaTemplate() {
    KafkaTemplate<String, WebUserOperation> kt = new KafkaTemplate<>(operationProducerFactory());
    kt.setProducerListener(new ProducerListener<String, WebUserOperation>() {

        @Override
        public void onSuccess(ProducerRecord<String, WebUserOperation> record, RecordMetadata recordMetadata) {
            System.out.println("### Callback :: " + recordMetadata.topic() + " ; partition = " 
                    + recordMetadata.partition()  +" with offset= " + recordMetadata.offset()
                    + " ; Timestamp : " + recordMetadata.timestamp() + " ; Message Size = " + recordMetadata.serializedValueSize());
        }

        @Override
        public void onError(ProducerRecord<String, WebUserOperation> producerRecord, Exception exception) {
            System.out.println("### Topic = " + producerRecord.topic() + " ; Message = " + producerRecord.value().getOperation());
            exception.printStackTrace();
        }
    });
    return kt;
}
@Bean
公共KafkaTemplate操作KafkaTemplate(){
KafkaTemplate kt=新的KafkaTemplate(operationProducerFactory());
kt.setProducerListener(新ProducerListener(){
@凌驾
成功时公开作废(生产记录、记录元数据、记录元数据){
System.out.println(“####回调::“+recordMetadata.topic()+”分区=”
+recordMetadata.partition()+”和offset=“+recordMetadata.offset()
+“时间戳:”+recordMetadata.Timestamp()+“消息大小=“+recordMetadata.serializedValueSize());
}
@凌驾
公共无效报告人(生产记录生产记录例外){
System.out.println(“###Topic=“+producerRecord.Topic()+”;Message=“+producerRecord.value().getOperation());
异常。printStackTrace();
}
});
返回kt;
}

下面的代码可以让我异步获得响应

    ProducerRecord<UUID, Person> person = new ProducerRecord<>(kafkaTemplate.getDefaultTopic(), messageKey,Person);
    Runnable runnable = () -> kafkaTemplate.send(person).addCallback(new MessageAckHandler());
    new Thread(runnable).start();

  public class MessageAckHandler implements ListenableFutureCallback<SendResult<UUID,Person>> {

    @Override
    public void onFailure(Throwable exception) {
      log.error("unable to send message: " + exception.getMessage());
     }

     @Override
     public void onSuccess(SendResult<UUID, ScreeningEvent> result) {
       log.debug("sent message with offset={} messageID={}", result.getRecordMetadata().offset(), result.getProducerRecord().key());
     }
  }

   public class SendResult<K, V> {

     private final ProducerRecord<K, V> producerRecord;

     private final RecordMetadata recordMetadata;

     public SendResult(ProducerRecord<K, V> producerRecord, RecordMetadata recordMetadata) {
        this.producerRecord = producerRecord;
        this.recordMetadata = recordMetadata;
    }

    public ProducerRecord<K, V> getProducerRecord() {
       return this.producerRecord;
    }

    public RecordMetadata getRecordMetadata() {
       return this.recordMetadata;
    }

    @Override
    public String toString() {
       return "SendResult [producerRecord=" + this.producerRecord + ", recordMetadata=" + this.recordMetadata + "]";
    }

 }
ProducerRecord person=新的ProducerRecord(kafkaTemplate.getDefaultTopic(),messageKey,person);
Runnable Runnable=()->kafkaTemplate.send(person.addCallback(newmessageackhandler());
新线程(runnable.start();
公共类MessageAckHandler实现ListenableFutureCallback{
@凌驾
失败时公共无效(可丢弃的例外){
log.error(“无法发送消息:+exception.getMessage());
}
@凌驾
成功时公共无效(SendResult结果){
log.debug(“发送的消息偏移量={}messageID={}”,result.getRecordMetadata().offset(),result.getProducerRecord().key());
}
}
公共类发送结果{
私人最终生产记录生产记录;
私有最终记录元数据;
公共发送结果(生产记录生产记录元数据记录元数据){
this.producerRecord=producerRecord;
this.recordMetadata=recordMetadata;
}
公共产品记录getProducerRecord(){
退回此.producerRecord;
}
public RecordMetadata getRecordMetadata(){
返回此.recordMetadata;
}
@凌驾
公共字符串toStr