Apache kafka 从Kafka Consumer内部,如何获取消息头

Apache kafka 从Kafka Consumer内部,如何获取消息头,apache-kafka,micronaut,micronaut-kafka,Apache Kafka,Micronaut,Micronaut Kafka,简而言之,我想让消费者听到消息头,但我得到的是“未指定必需参数[header]” 以下是我尝试失败的原因: package com.tolearn.consumer import io.micronaut.configuration.kafka.Acknowledgement import io.micronaut.configuration.kafka.annotation.KafkaKey import io.micronaut.configuration.kafka.annotation

简而言之,我想让消费者听到消息头,但我得到的是“未指定必需参数[header]”

以下是我尝试失败的原因:

package com.tolearn.consumer

import io.micronaut.configuration.kafka.Acknowledgement
import io.micronaut.configuration.kafka.annotation.KafkaKey
import io.micronaut.configuration.kafka.annotation.KafkaListener
import io.micronaut.configuration.kafka.annotation.OffsetStrategy
import io.micronaut.configuration.kafka.annotation.Topic
import io.micronaut.messaging.annotation.Header

@KafkaListener(
        groupId="myGroup",
        offsetStrategy=OffsetStrategy.SYNC_PER_RECORD
)
class DemoConsumer {
    @Topic("demotopic")
    fun receive(@KafkaKey key: String?,
                msg: String,
                header: Header,
                acknowledgement: Acknowledgement

    ){
        println("Key = $key " +
               "msg = $msg " +
                "header = $header"
       )

        acknowledgement.ack();
    }
}
它将打印此错误:

00:02:05.890 [consumer-executor-thread-1] ERROR i.m.c.k.e.KafkaListenerExceptionHandler - Error processing record [Optional[ConsumerRecord(topic = demotopic, partition = 0, leaderEpoch = 0, offset = 6, CreateTime = 1607828525818, serialized key size = 36, serialized value size = 14, headers = RecordHeaders(headers = [RecordHeader(key = My-Header, value = [109, 121, 72, 101, 97, 100, 101, 114])], isReadOnly = false), key = 2afe3f0d-40c0-44f6-93a3-cce06678df80, value = name: "Hello"
)]] for Kafka consumer [com.tolearn.consumer.DemoConsumer@4ca2237e] produced error: Required argument [Header header] not specified
io.micronaut.core.bind.exceptions.UnsatisfiedArgumentException: Required argument [Header header] not specified
    at io.micronaut.core.bind.DefaultExecutableBinder.bind(DefaultExecutableBinder.java:88)
    at io.micronaut.configuration.kafka.processor.KafkaConsumerProcessor.lambda$process$8(KafkaConsumerProcessor.java:494)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
如果它添加了一些内容,以下是制作人:

package com.tolearn.producer

import io.micronaut.configuration.kafka.annotation.KafkaClient
import io.micronaut.configuration.kafka.annotation.KafkaKey
import io.micronaut.configuration.kafka.annotation.Topic
import io.micronaut.messaging.annotation.Header

@KafkaClient(
        id = "demo-producer",
        acks = KafkaClient.Acknowledge.ALL)
public interface DemoProducer {

    @Topic("demotopic")
    fun sendDemoMsg(
            @KafkaKey key: String?,
            @Header("My-Header") myHeader: String,
            msg: String?) {
    }

}
使用头抛出生产者发送消息的服务

package com.tolearn.service

import com.tolearn.producer.DemoProducer
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

@Singleton
class DemoService {
    @Inject
    @Named("dp")
    lateinit var dp : DemoProducer

    fun postDemo(key: String, msg: String){
        //blocking
        dp.sendDemoMsg(key, "myHeader", msg)
    }
}
我想打印。。。我的头:我的头

***编辑

对于未来的读者,我的soluiton成为:

应用程序

micronaut:
  application:
    name: demoGrpcKafka
  executors:
    consumer:
      type: fixed
      nThreads: 1
#kafka.bootstrap.servers: localhost:9092
kafka:
  bootstrap:
    servers: localhost:9092
  consumers:
    default:
      auto:
        commit:
          enable: false
  producers:
    #default:
    demo-producer:
      retries: 2
my:
  application:
    token: tokenFromYml
制作人

package com.tolearn.producer

import io.micronaut.configuration.kafka.annotation.KafkaClient
import io.micronaut.configuration.kafka.annotation.KafkaKey
import io.micronaut.configuration.kafka.annotation.Topic
import io.micronaut.messaging.annotation.Header

@KafkaClient(
        id = "demo-producer",
        acks = KafkaClient.Acknowledge.ALL)
        @Header(name = "X-Token", value = "\${my.application.token}")
public interface DemoProducer {

    @Topic("demotopic")
    fun sendDemoMsg(
            @KafkaKey key: String?,
            msg: String?) {
    }

}
消费者

package com.tolearn.consumer

import io.micronaut.configuration.kafka.Acknowledgement
import io.micronaut.configuration.kafka.KafkaHeaders
import io.micronaut.configuration.kafka.annotation.KafkaKey
import io.micronaut.configuration.kafka.annotation.KafkaListener
import io.micronaut.configuration.kafka.annotation.OffsetStrategy
import io.micronaut.configuration.kafka.annotation.Topic
import io.micronaut.messaging.MessageHeaders


@KafkaListener(
        groupId="myGroup",
        offsetStrategy=OffsetStrategy.SYNC_PER_RECORD
)
class DemoConsumer {
    @Topic("demotopic")
    fun receive(@KafkaKey key: String?,
                msg: String,
                headers: MessageHeaders,
                acknowledgement: Acknowledgement,
                offset: Long,
                partition: Int,
                topic: String,
                timestamp: Long

    ){
        val h = (headers).get("X-Token")

        println("Key = $key " +
               "msg = $msg " +
                "offset = $offset " +
                "partition = $partition " +
                "topic = $topic " +
                "timestamp = $timestamp " +
                "header = $h"
       )

        acknowledgement.ack();
    }
}

看起来您应该尝试使用io.micronaut.messaging.MessageHeaders,而不仅仅是kafka Header类

@Topic("demotopic")
fun receive(@KafkaKey key: String?,
            msg: String,
            headers: MessageHeaders,
            acknowledgement: Acknowledgement)
此外,您还可以在生产者端使用消费者中的@io.micronaut.messaging.annotation.header(“我的头”)来指定头的值

如果头是可选的,或者只是字符串,不要忘记添加@javax.annotation.Nullable?以科特林为例

@Topic("demotopic")
fun receive(@KafkaKey key: String?,
            msg: String,
            @Nullable @Header("My-Header") myHeader: String,
            acknowledgement: Acknowledgement)

看起来您应该尝试使用io.micronaut.messaging.MessageHeaders,而不仅仅是kafka Header类

@Topic("demotopic")
fun receive(@KafkaKey key: String?,
            msg: String,
            headers: MessageHeaders,
            acknowledgement: Acknowledgement)
此外,您还可以在生产者端使用消费者中的@io.micronaut.messaging.annotation.header(“我的头”)来指定头的值

如果头是可选的,或者只是字符串,不要忘记添加@javax.annotation.Nullable?以科特林为例

@Topic("demotopic")
fun receive(@KafkaKey key: String?,
            msg: String,
            @Nullable @Header("My-Header") myHeader: String,
            acknowledgement: Acknowledgement)

谢谢现在,在我将io.micronaut.messaging.MessageHeaders添加到consumer上之后,它不会弹出更多的执行选项。您知道如何将标题值转换为字符串吗?标题带有“RecordHeader(key=X-Token,value=[1161111107101110,70114111109,89109108])。[116、111、107、101、110、70、114、111、109、89、109、108]必须与此单词“tokenFromYml”等效。嗯,我尝试了headers.get(0).toString()但是我得到了“整数文本不符合预期的类型CharSequence!”现在它正在使用(headers.get)(“X-Token”)。我将为未来的读者添加以上所有内容。你已经回答了我的问题。如果您不介意的话,最后一条评论是:使用io.micronaut.messaging.MessageHeaders(您的第一个代码)和io.micronaut.messaging.annotation.Header(您的第二个代码)之间有什么区别?Yeap头值将以字节为单位,这是因为Kafka以字节为单位序列化头值,您可以通过新字符串轻松转换为sting(bytes,StandardCharsets.UTF-8)所以对于第一个,这就是为消费者注册的Micronaut头值,并且可以被注入(有特定的注册类型,MessageHeader就是其中之一),对于第二个,这基本上是+-相同的机制,更具抽象性,它还使用MessageHeaders.get(“我的头”)在带有验证的钩子下,只是为了简化,2个案例可以通过注释进行验证。谢谢。现在在消费者上添加io.micronaut.messaging.MessageHeaders后,它不会弹出更多的执行选项。请注意,您知道如何将标题值转换为字符串吗?标题带有“RecordHeader(key=X-Token,value=[116、111、107、101、110、70、114、111、109、89、109、108])“[116、111、107、101、110、70、114、111、109、89、109、108]必须等同于这个单词“tokenFromYml”。我尝试了headers.get(0).toString()但是我得到了“整数文字不符合预期的类型CharSequence!”现在它正在使用(headers.get(“X-Token”)。我将在上面添加完整的soluiton供未来读者阅读。您已经回答了我的问题。如果您不介意的话,最后一句话是:使用io.micronaut.messaging.MessageHeader(您的第一个代码)和io.micronaut.messaging.annotation.Header(您的第二个代码)之间有什么区别?Yeap头值将以字节为单位,这是因为Kafka以字节为单位序列化头值,您可以通过新字符串(字节,StandardCharsets.UTF-8)轻松转换为sting,因此对于第一个字符串,即Micronaut头值是为消费者注册的,并且可以注入(有特定的注册类型,MessageHeader就是其中之一)对于第二个,这基本上是+相同的机制,具有更多的抽象,它还使用MessageHeaders.get(“MyHeader”)在钩子下进行验证,只是为了简化,2个案例可以通过注释进行验证。