Kafka Consumer如何以及何时使用Apache Camel之类的EIP框架在Kafka中提交偏移量?我们如何使用camel-kakfa异步处理重试?

时间:2018-12-27 08:14:38

标签: java apache-kafka apache-camel kafka-consumer-api

路由构建器的示例代码如下:

    // For out of seq event state (reque)
    onException(OutOfSeqStateException.class)
    .logStackTrace(false).logExhaustedMessageHistory(false)
        .setHeader("eventSource", constant(EventConstants.BACKEND))
        .delay(30000)/*.method(DelayerBean.class , "computeDelayInMillis")*/.asyncDelayed().// delay should be asynchronously
        .setBody().header(EventConstants.BE_STATE_EVENT)// send original event
        .to("direct:requeue");// toendpoint: requeroute

    // For handling other exceptions
    onException(Exception.class)
    .log("EXCEPTION OCCURED.....   ->  \"${exception.message}\"")
        .setExchangePattern(ExchangePattern.InOnly)
        .bean(KafkaErrorHandlerBean.class, "handle")
        .handled(true);

    // Backend Events Route
    from(commonCamelConfig.getKafkaConsumerEndpoint())
     .routeId("BackendStateIncomingRoute")
            .id(routeId)
            .to("log:" + fqClassName + "?showAll=true&level=" + logLevel)
            .unmarshal(jdf)
            .bean(MandatoryFieldCheckerBean.class, "performNullCheck")
            // all context info must be present,if not, throw exception
            .bean(ValidateEventHandlerBean.class, "validateIncomingEvents")
            .choice()
            .when().simple("${in.header.isValidEvent} == true",Boolean.class)//enter if valid event(backendstate/backenddata)
                    .choice()
                        .when(header("BEStateEvent").isNotNull())
                            .bean(EventTransformer.class, "getBackendTransformedEvent")
                            .bean(PaymentsService.class, "processMessage")
                            .bean(TransitionalStateHandlerBean.class,"handle")
                            .bean(AMQPProducer.class, "sendEventToMQ")
                            .setExchangePattern(ExchangePattern.InOnly)
                        .otherwise()
                            .bean(EventTransformer.class, "getBackendTransformedEvent")
                            .bean(PaymentsService.class, "processMessage")
                    .endChoice()
            .setExchangePattern(ExchangePattern.InOnly)//acknowledge only valid events, doesnt expect a reply
            .endChoice()
            .end();

    //Reque the original event in case of Retryable Exceptions
    from("direct:requeue").routeId("BackendDirectRequeRoute")
    .bean(RequestRetryHandlerBean.class, "doRetry")
    .to(commonCamelConfig.getKafkaConsumerEndpoint())
    .end();

使用者端点的Kafka配置如下:

    public String getKafkaConsumerEndpoint() {
    return properties.getJmsKafkaBroker()
            + ":" + properties.getKafkaPaymentsOtpTopic()
            + "?brokers="+ properties.getBootstrapServers()
            + "&groupId="+ properties.getGroupId()
            + "&autoOffsetReset="+ properties.getAutoOffsetReset()
            + "&autoCommitEnable=true"
            + "&keyDeserializer=org.apache.kafka.common.serialization.StringDeserializer"
            + "&valueDeserializer=org.apache.kafka.common.serialization.StringDeserializer";
}

关于上述代码,我有两个查询:

  1. 在整个路由的哪一点,kafka使用者是提交偏移量还是一项独立的任务,因为我尚未修改字段 autoCommitIntervalMs 的默认值5秒这是否意味着它会每5秒独立提交一次。

  2. 在发生序列外异常的情况下,我想处理请求场景,而我这样做的方法是通过创建另一个具有延迟模式逻辑的端点,该端点将在最大请求时间后将错误消息发送给DLQ。上述逻辑中有哪些漏洞,有没有更好的方法来处理?如果这个逻辑看起来不错,那么请查看我使用了 asyncdelayed()进行异步延迟,但是它似乎无法正常工作并阻止新消息,直到被请求为止。请帮助我实现异步延迟。

1 个答案:

答案 0 :(得分:0)

1)如果使用的骆驼版本<2.22,则无法控制偏移提交,它发生在默认值为5秒且可以更改的另一个线程中。如果您使用的骆驼版本> = 2.22,则只有您可以控制消息的手动提交。要使用手动提交,请设置以下属性:

autoCommitEnable = false:关闭偏移量的自动提交,以便我们可以使用手动提交。 allowManualCommit = true:打开手动提交,使我们能够使用KafkaManualCommit功能。 下面是代码片段:

boost::lockfree::queue<std::string> message_queue;

void producer() {
    //...
    message_queue.push("A string to print!");
    //...
}

void mexFunction( /*...*/ ) {
    // ...
    boost::thread producer_thread(producer);
    boost::thread consumer_thread(consumer);
    while(producer_thread.joinable()) {
        join_for(boost::chrono::milliseconds(50));
        std::string s;
        while (message_queue.pop(s)) {
            mexPrintf("%s\n", s.c_str());
        }
    }
    producer_thread.join();
    done = true;
    consumer_thread.join();
    // ...
}

2)在第二个问题中,您似乎想再次将消息放回kafka进行处理。但是从您的代码看来,您正在为消费者和生产者使用相同的终结点。当您想在kafka中生成消息时,需要指定消息中未显示的消息的“主题”,“分区”和“键”。讨论漏洞,因为您将消息再次放入kafka中,如果消息已损坏怎么办,因此您将继续获取相同的异常并将相同的消息再次放入kafka中。我建议以相同的路径重试该消息。下面是代码片段:

KafkaManualCommit manual =
                        exchange.getIn().getHeader(KafkaConstants.MANUAL_COMMIT, KafkaManualCommit.class);
                if (manual != null) {
                    LOGGER.info("committing the offset manually");
manual.commitSync();
            }