卡夫卡消费者空白记录

时间:2020-07-08 09:10:37

标签: java apache-kafka kafka-consumer-api jaas confluent-cloud

有关此主题的问题很多,但这不是重复的问题!

我面临的问题是我试图用Java 14和Kafka 2.5.0设置SpringBoot项目,而我的 Consumer返回一个空的记录列表。 这里的大多数答案都表明了poll frequently或设置offset mode to earliest的一些被遗忘的属性。

即使我的配置设置似乎非常规,我也看不到与docs.confluent.io的任何逻辑差异(请参见下面的代码段中的jaas.conf设置)。< / p>

@EnableKafka
@Configuration
public class KafkaConfig {

    @Bean
    public KafkaConsumer<Long, MyClass> consumerConfigs() {
        Properties config = new Properties();

        config.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
        config.put(SaslConfigs.SASL_MECHANISM, "PLAIN");

        config.put(ConsumerConfig.CLIENT_ID_CONFIG, "serviceName");
        config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "confluent-cloud");
        config.put(ConsumerConfig.GROUP_ID_CONFIG, "serviceName");
        config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class);
        config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, MyClass.class);
        config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        config.put(ConsumerConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, 300_000);

        System.setProperty("java.security.auth.login.config", ".\\src\\main\\resources\\jaas.conf");

        return new KafkaConsumer<>(config);
    }
}

这有效。我没有收到任何异常(Kafka或其他),并且已建立连接。

// jaas.conf-file
KafkaClient {
org.apache.kafka.common.security.plain.PlainLoginModule required
serviceName="serviceName"
username="username"
password="password";
};

这是我实际进行轮询的地方:

try {
            KafkaConsumer<Long, MyClass> consumer = kafkaConfig.consumerConfigs();
            consumer.subscribe(Collections.singletonList(inputTopic));

            int count = 0;
            Long start = System.currentTimeMillis();
            Long end = System.currentTimeMillis();

            while (end - start < 900_000) { 
                // boolean would be set to true in production
                ConsumerRecords<Long, MyClass> records = consumer.poll(Duration.ofMillis(1000));
                records.forEach(record -> {
                    MyOtherClass result = myOwnKafkaService.getSomethingFromRecord(record.key(), record.value());
                    System.out.println(result);
                });
               
                consumer.commitSync();

                System.out.println("visualize number of loops made: " + ++count);
                end = System.currentTimeMillis();
            }
        } catch (KafkaException e) {
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

我添加了照片和其他 clutter 以便尝试查找问题。我在调试模式下运行程序,并将断点放在此行上:

MyOtherClass result = myOwnKafkaService.getSomethingFromRecord(record.key(), record.value());

结果,正如人们所期望的那样,我看到了一条打印的行,它每秒计数一次。但是,由于我的使用者不返回任何记录,因此它永远不会输入forEach,因此也不会触发我的断点。

我绝对可以在云中看到我的主题,有两个分区。消息是源源不断地产生的,所以我知道我应该能够拿起一些东西。

我知道连接到集群需要一些时间,但是由于当前时间设置为四分之一小时,我应该至少收到一些东西,对吗?作为一种替代方法,我尝试将consumer.subscribe()切换为consumer.assign()方法,即指定了TopicPartition,将使用者设置为consumer.seekToBeginning()。运行正常,但什么也没返回。

在最常见的示例中找不到的另一件事是,我使用了自己的类。因此,我根据this tutorial实现了自定义(反)序列化器,而不是KafkaConsumer<String, String>

可以是我的配置设置吗?轮询超时出了点问题? (反)序列化,还是其他?我真的无法查明为什么我得到零记录的任何原因。任何反馈将不胜感激!

1 个答案:

答案 0 :(得分:0)

问题解决了。尽管如此,您无法从我发布的问题中确定什么,但是如果有人发现自己陷入了类似的配置中,我想澄清一些事情。

  1. 验证收到的密码确实是正确的密码。 人脸

是这样,我以为他正在与集群建立连接,但是我的循环继续打印计数,因为执行了.poll(Duration.ofMillis(1000))方法->检查他是否可以在给定的超时时间内连接->如果连接失败,则继续返回零条记录。没有引发任何错误。通常,大约2秒钟后,就应该建立连接。

  1. 检查您与数据库的连接。

您从不希望应用程序停止,这就是为什么我设计了myOwnKafkaService.getSomethingFromRecord(record.key(), record.value())方法来记录所有错误,但捕获了所有异常的原因。直到我检查日志后,我才意识到访问远程数据库的权限不正确。

  1. 所谓的时间戳记,应反序列化为java.util.Date

错误地解析它会引发异常,但是我的方法返回了null。正如此答案中的所有评论一样,这一问题也归结为这种设置没有经验。您将在下面找到经过纠正的类作为工作示例(但绝不是完全的最佳实践)。

KafkaConfig:

@EnableKafka
@Configuration
public class KafkaConfig {

    @Bean
    public KafkaConsumer<Long, MyClass> consumerConfigs() {
        Properties config = new Properties();

        config.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, "SASL_SSL");
        config.put(SaslConfigs.SASL_MECHANISM, "PLAIN");

        config.put(ConsumerConfig.CLIENT_ID_CONFIG, "serviceName");
        config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "confluent-cloud");
        config.put(ConsumerConfig.GROUP_ID_CONFIG, "serviceName");
        config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class);
        config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, MyClass.class);
        config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        config.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 100_000);
        config.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 300_000);
        config.put(ConsumerConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, 300_000);

        System.setProperty("java.security.auth.login.config", ".\\src\\main\\resources\\jaas.conf");

        return new KafkaConsumer<>(config);
    }
}

轮询方法的主体:

            KafkaConsumer<Long, MyClass> consumer = kafkaConfig.consumerConfigs();
            consumer.subscribe(Collections.singletonList(inputTopic));

            while (true) {
                ConsumerRecords<Long, MyClass> records = consumer.poll(Duration.ofMillis(1000));
                records.forEach(record -> {
                    MyOtherClass result = myOwnKafkaService.getSomethingFromRecord(record.key(), record.value());
                    System.out.println(result);
                });
                consumer.commitSync();
            }

带有反序列化器的MyClass的小示例:

@Data
@Slf4J
public class MyClass implements Deserializer<MyClass> {

    @JsonProperty("UNIQUE_KEY")
    private Long uniqueKey;
    @JsonProperty("EVENT_TIMESTAMP")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS")
    private Date eventTimestamp;
    @JsonProperty("SOME_OTHER_FIELD")
    private String someOtherField;

@Override
    public MyClass deserialize(String s, byte[] bytes) {
        ObjectMapper mapper = new ObjectMapper();
        MyClass event = null;
        try {
            event = mapper
                    .registerModule(new JavaTimeModule())
                    .readValue(bytes, MyClass.class);
        } catch (Exception e) {
            log.error("Something went wrong during the deserialization of the MyClass: {}", e.getMessage());
        }
        return event;
    }
}

我希望这会在将来为其他人服务。我从挫折和错误中学到了很多东西。