从Kafka中的民意调查记录创建批次

时间:2019-01-22 12:19:28

标签: java apache-kafka apache-kafka-connect

我们需要对弹性搜索执行批量写入。我们想知道是否有更好的批处理数据方式,并避免了批处理时数据丢失

 public void consume() {
        logger.debug("raw consume......");

        String topic = "json.incoming";
        String consGroup = "rConsumerGroup";

        Properties props = new Properties();
        props.put("enable.auto.commit", "false");
        props.put("session.timeout.ms", "20000");
        props.put("max.poll.records", "10000");

        consumer = new GenericConsumer<String, JsonNode>().initialize(topic, consGroup, STREAMSERDE.STRINGDESER, STREAMSERDE.JSONDESER, props);
        logger.debug("Kafka Consumer Initialized......");
        buffer = new ArrayList<MessageVO>();

        while (true) {
            try {
                ConsumerRecords<String, JsonNode> records = consumer.poll(100);
                Date startTime = Calendar.getInstance()
                    .getTime();
                if (records.count() == 0 && !buffer.isEmpty()) {
                    lastSeenZeroPollCounter++;
                }
                if (records.count() > 0) {
                    logger.debug(">>records count = " + records.count());
                    for (ConsumerRecord<String, JsonNode> record : records) {
                        logger.debug("record.offset() = " + record.offset() + " : record.key() = " + record.key());
                        JsonNode jsonMessage = record.value();
                        logger.debug("incoming Message = " + jsonMessage);
                        ObjectMapper objectMapper = new ObjectMapper();
                        MessageVO rawMessage = objectMapper.convertValue(jsonMessage, MessageVO.class);
                        logger.info("Size of the buffer is " + buffer.size());
                        buffer.add(rawMessage);
                    }
                    Date endTime = Calendar.getInstance()
                        .getTime();
                    long durationInMilliSec = endTime.getTime() - startTime.getTime();
                    logger.debug("Number of Records:: " + records.count() + " Time took to process poll :: " + durationInMilliSec);
                }
                if ((buffer.size() >= 1000 && buffer.size() <= 3000) || (buffer.size() > 0 && lastSeenZeroPollCounter >= 3000)) {
                    lastSeenZeroPollCounter = 0;
                    List<RawSyslogMessageVO> clonedBuffer = deepCopy(buffer);
                    logger.info("The size of clonedBuffer is ::: " + clonedBuffer.size());
                    writerService.writeRaw(clonedBuffer);
                    buffer.clear();
                }

                consumer.commitSync();
            } catch (Throwable throwable) {
                logger.error("Error occured while processing message", throwable);
                throwable.printStackTrace();
            }
        }
    }

用于克隆数据以避免丢失数据的代码

 private List<MessageVO> deepCopy(List<MessageVO> messages) {
        List<MessageVO> listOfMessages = new ArrayList<>();
        logger.debug("DeepClone :: listOfMessages size ::: " + listOfMessages.size());
        listOfMessages.addAll(messages);
        return Collections.unmodifiableList(messages);
    }

感谢您的帮助。谢谢。

2 个答案:

答案 0 :(得分:1)

比自己编写更好的方法是使用Apache Kafka的Kafka Connect API-它是专门为从系统到Kafka的流集成以及从Kafka到其他系统的流集成而构建的:-)

Elasticsearch connector会将数据从Kafka主题流传输到Elasticsearch,并具有可配置的批处理大小等,以及一次精确的传递语义,可伸缩的处理等。

免责声明:我为Confluent工作。

答案 1 :(得分:0)

我们通过稍微简化应用程序的设计来处理相同的用例:我们基本上执行以下步骤

  1. 使用Spring Kafka BatchAcknowledgingMessageListener进行抓取     根据要求设置了max.poll.records的记录
  2. 对于每次提取,请使用Elasticsearch BulkRequest API提交消息
  3. 成功进行批量索引后,请确认Kafka。
  4. 失败时重试或处理错误

通过采用这种简单的设计,我们意识到大多数批量提交将具有所需的记录数。对于Kafka主题没有为批量索引所需的记录数目多的消息的情况,我们决定在任何情况下都对单个访存中可用的任何内容进行索引,而不是在应用程序中显式处理提交状态,管理缓冲区等。

Elasticsearch批量提交是一种优化-我找不到任何理由可以精确说明每个单个批量请求的记录总数。 (也请参见this guide)。

PS:我们需要编写代码而不是使用连接器或现成的解决方案,因为我们的输入来自不同主题的多种主题,例如protobuf,zip XML,Json等,我们需要在进行索引之前进行格式转换和复杂的反序列化数据