Spring Cloud【有与无】【SKB1】Apache Kafka Binder

版本

spring-cloud-stream-binder-kafka

3.0.6.RELEASE GA

参考指南

本指南介绍了Apache Kafka实现的Spring Cloud Stream Binder。 它包含有关其设计,用法和配置选项的信息,以及有关Stream Cloud Stream概念如何映射到Apache Kafka特定结构的信息。 此外,本指南还介绍了Spring Cloud Stream的Kafka Streams绑定功能。

1.Apache Kafka Binder

1.1.用法

要使用Apache Kafka binder,您需要将spring-cloud-stream-binder-kafka添加为Spring Cloud Stream应用程序的依赖项,如以下Maven示例所示:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>

另外,您也可以使用Spring Cloud Stream Kafka Starter,如以下Maven示例所示:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>

1.2.总览

下图显示了Apache Kafka binder的工作方式的简化图:

卡夫卡粘合剂

图 1. Kafka Binder

Apache Kafka Binder实现将每个目标(destination)映射到一个Apache Kafka Topic。 消费者组直接映射到相同的Apache Kafka概念(concept)。 分区也直接映射到Apache Kafka分区(partitions )。

当前binder使用版本为2.3.1的 Apache Kafka kafka-clients。 该客户端可以与较旧的代理进行通信(请参阅Kafka文档),但是某些功能可能不可用。 例如,对于低于0.11.x.x的版本,不支持本机头。 另外,0.11.x.x不支持autoAddPartitions属性。

1.3.配置选项

本节包含Apache Kafka Binder使用的配置选项。

有关与Binder有关的常见配置选项和属性,请参阅core documentation

1.3.1.Kafka Binder属性

spring.cloud.stream.kafka.binder.brokers

           Kafka binder 连接到的代理(brokers)列表。

           默认值: localhost

spring.cloud.stream.kafka.binder.defaultBrokerPort

           代理(brokers)允许指定带有或不带有端口信息的主机(例如,host1,host2:port2)。 当在代理(broker)列表中未配置任何端口时,这将设置默认端口。

           默认值: 9092.

spring.cloud.stream.kafka.binder.configuration

           以Key/Value映射的客户端属性(包括生产者和消费者)传递给通过代理(binder)程序所创建的所有客户端。 由于生产者和消费者都使用了这些属性,因此应将使用限制为通用属性,例如安全设置。 通过此配置提供的未知的Kafka生产者或使用者属性将被过滤掉,并且不允许传播。 此处的属性取代引导中设置的所有属性。

           默认值: Empty map.

spring.cloud.stream.kafka.binder.headers

           binder传输的自定义标头列表。 仅当与Kafka客户端版本<0.11.0.0的旧版应用程序(⇐1.3.x)通信时才需要。 较新的版本本机支持标头。

           默认值: empty.

spring.cloud.stream.kafka.binder.healthTimeout

           等待获取分区(partition)信息的时间,以秒为单位。 如果此计时器到期,运行状况将报告为已关闭。

           默认值: 10.

spring.cloud.stream.kafka.binder.requiredAcks

           代理(broker)程序上必需acks的确认数。 有关生产者acks属性的信息,请参阅Kafka文档。

           默认值: 1

spring.cloud.stream.kafka.binder.minPartitionCount

           仅在设置了autoCreateTopicsautoAddPartitions时有效。 binder在生成或使用数据的主题上配置的全局最小分区(partitions)数。 它可以由生产者的partitionCount设置或生产者的 instanceCount * concurrency 设置的值(如果任一个较大)代替。

           默认值: Empty map.

spring.cloud.stream.kafka.binder.replicationFactor

          如果autoCreateTopics是启动的,则自动创建的主题的复制因子。 可以在每个绑定上覆盖。

          默认值: 1

spring.cloud.stream.kafka.binder.autoCreateTopics

          如果设置为true,则binder将自动创建新主题。 如果设置为false,则binder依赖于已配置的主题。 在后一种情况下,如果主题不存在,则binder无法启动。

          默认值: true

此设置与代理(broker)的auto.create.topics.enable设置无关,并且不影响它。 如果服务器设置为自动创建主题,则可以使用默认代理(broker)设置将它们作为元数据检索请求的一部分进行创建。

spring.cloud.stream.kafka.binder.autoAddPartitions

          如果设置为true,则binder将根据需要创建新分区。 如果设置为false,则binder依赖于已配置的主题的分区大小。 如果目标主题的分区数小于预期值,则binder无法启动。

          默认值: false

spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix

          在binder中启用事务。 请参阅Kafka文档中的transaction.idspring-kafka文档中的Transactions。 启用事务后,单个生产者属性将被忽略,所有生产者都将使用spring.cloud.stream.kafka.binder.transaction.producer.*属性。

          默认值: null (无事务)

spring.cloud.stream.kafka.binder.transaction.producer.*

          在事务代理(binder)中生产者的全局生产者属性。 请参阅spring.cloud.stream.kafka.binder.transaction.transactionIdPrefixKafka Producer Properties以及所有代理(binder)程序支持的常规生产者属性。

          默认值:请参见各个生产者属性。

spring.cloud.stream.kafka.binder.headerMapperBeanName

          KafkaHeaderMapper的Bean名称,用于在spring-messaging头和Kafka头之间进行映射。 例如,如果您希望在使用JSON反序列化头的BinderHeaderMapper的bean中自定义受信任的程序包,请使用此方法。 如果使用此属性使绑定程序无法使用此自定义BinderHeaderMapper的Bean,则绑定程序将查找名称为kafkaBinderHeaderMapper的标头映射器bean,其名称为BinderHeaderMapper类型,然后回退到由binder程序创建的默认BinderHeaderMapper。

          默认值:无

1.3.2.Kafka Consumer属性

为了避免重复,Spring Cloud Stream支持以spring.cloud.stream.kafka.default.consumer.<property>=<value>的格式为所有通道设置值。

以下属性仅适用于Kafka使用者,并且必须加上前缀

spring.cloud.stream.kafka.bindings.<channelName>.consumer.

 

admin.configuration

          从2.1.1版开始,不推荐使用此属性,而推荐使用topic.properties,并且在将来的版本中将不再支持该属性。

admin.replicas-assignment

          从2.1.1版开始,不推荐使用此属性,而推荐使用topic.replicas-assignment,并且在将来的版本中将删除对此属性的支持。

admin.replication-factor

          从2.1.1版开始,不推荐使用此属性,而推荐使用topic.replication-factor,并且在以后的版本中将不再支持该属性。

autoRebalanceEnabled

          设置为true时,主题分区将在消费者组的成员之间自动重新平衡。 如果为false,则根据spring.cloud.stream.instanceCountspring.cloud.stream.instanceIndex为每个消费者分配固定的一组分区。 这要求在每个启动的实例上都正确设置spring.cloud.stream.instanceCountspring.cloud.stream.instanceIndex属性。 在这种情况下,spring.cloud.stream.instanceCount属性的值通常必须大于1。

          默认值:true

ackEachRecord

          当autoCommitOffset为true时,此设置指示在处理每个记录后是否提交偏移量。 默认情况下,在处理了consumer.poll()返回的记录批次中的所有记录之后,才提交偏移量。 可以通过max.poll.records Kafka属性控制轮询返回的记录数,该属性是通过使用者配置属性设置的。 将此设置为true可能会导致性能下降,但是这样做会减少发生故障时重新传递记录的可能性。 另外,请参见binder requiredAcks属性,该属性还影响落实偏移量的性能。

          默认值:false

autoCommitOffset

          处理消息后是否自动提交偏移量。 如果设置为false,则入站消息中将出现带有类型为org.springframework.kafka.support.Acknowledgment的键为kafka_acknowledgment的标头。 应用程序可以使用此标头来确认消息。 有关详细信息,请参见示例部分。 当此属性设置为false时,Kafka binder 将ack模式设置为org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMode.MANUAL,应用程序负责确认记录。 另请参见ackEachRecord

          默认值:true

autoCommitOnError

          仅当autoCommitOffset设置为true时才有效。 如果设置为false,它将抑制导致错误的消息的自动提交,仅对成功的消息进行提交。 如果持续出现故障,它允许流从上次成功处理的消息自动重播。 如果设置为true,它将始终自动提交(如果启用了自动提交)。 如果未设置(默认值),则它实际上具有与enableDlq相同的值,如果将错误消息发送到DLQ,则自动提交错误消息,否则不提交。

          默认值:未设置

resetOffsets

          是否将使用者的偏移量重置为startOffset提供的值。 如果提供了KafkaRebalanceListener,则必须为false;否则为false。 请参阅使用KafkaRebalanceListener

          默认值:false

startOffset

          新组的起始偏移量。 允许的值:earliest 和 latest。 如果为消费者“binding”显式设置了消费者组(通过spring.cloud.stream.bindings.<channelName>.group),则将'startOffset' 设置为earliest 。 否则,对于anonymous(匿名)消费者组,它设置为earliest。 另请参见resetOffsets (在此列表的前面)。

          默认值:null(相当于最早的)

enableDlq

          设置为true时,它将为使用者启用DLQ行为。 默认情况下,导致错误的消息将转发到名为error.<destination>.<group>的主题。 可以通过设置dlqName属性来配置DLQ主题名称。 当错误数量相对较小并且重放整个原始主题可能太麻烦时,这为更常见的Kafka重播方案提供了一个替代选项。 有关更多信息,请参见 Dead-Letter Topic Processing。 从2.0版开始,发送到DLQ主题的消息通过以下标头得到增强:x-original-topic,x-exception-message和x-exception-stacktrace 作为 byte []。 默认情况下,失败的记录将发送到DLQ主题中与原始记录相同的分区号。 有关如何更改该行为的信息,请参见 Dead-Letter Topic Partition Selection当destinationIsPattern为true时不允许

          默认值:false

dlqPartitions

          当enableDlq为true且未设置此属性时,将创建一个死信主题,该主题具有与主要主题相同的分区数。 通常,死信记录将被发送到死信主题中与原始记录相同的分区。 这种行为可以改变。 请参阅 Dead-Letter Topic Partition Selection。 如果此属性设置为1,并且没有DqlPartitionFunction的bean,则所有死信记录都将写入分区0。如果此属性大于1,则必须提供DlqPartitionFunction的bean。 请注意,实际分区数受binder的minPartitionCount属性影响。

          默认值:无

configuration

          使用包含通用Kafka 消费者属性的键/值对进行映射。 除了具有Kafka 消费者属性外,还可以在此处传递其他配置属性。 例如,应用程序所需的一些属性,例如spring.cloud.stream.kafka.bindings.input.consumer.configuration.foo=bar

          默认值:Empty map

dlqName

          接收错误消息的DLQ主题的名称。

          默认值:null(如果未指定,则导致错误的消息将转发到名为error.<destination>.<group>的主题)

dlqProducerProperties

          使用此功能,可以设置特定于DLQ的生产者属性。 通过kafka生产者属性可用的所有属性都可以通过该属性设置。 在消费者上启用本机解码后(如useNativeDecoding: true),应用程序必须为DLQ提供相应的键/值序列化器。 必须以dlqProducerProperties.configuration.key.serializerdlqProducerProperties.configuration.value.serializer的形式提供。

          默认值:默认的Kafka生产者属性

standardHeaders

          指示入站通道适配器填充哪些标准头。 允许的值:noneidtimestampboth。 如果使用本机反序列化并且第一个组件接收消息需要一个id(例如配置为使用JDBC消息存储的聚合器),则很有用。

          默认值:none

converterBeanName

          实现RecordMessageConverter的bean的名称。 在入站通道适配器中用于替换默认的MessagingMessageConverter。

          默认值:null

idleEventInterval

          事件之间的间隔(以毫秒为单位),指示最近没有收到消息。 使用ApplicationListener <ListenerContainerIdleEvent>接收这些事件。 有关用法示例,请参见 Example: Pausing and Resuming the Consumer

          默认值:30000

destinationIsPattern

          如果为true,则将目的地视为正则表达式Pattern,用于由代理匹配主题名称。 如果为true,则不供应主题,并且不允许enableDlq,因为binder程序在供应阶段不知道主题名称。 请注意,检测与模式匹配的新主题所需的时间由消费者属性meta.max.age.ms控制,该属性(在撰写本文时)默认为300,000ms(5分钟)。 可以使用上面的配置属性来配置。

          默认值:false

topic.properties

          设置新主题时使用的Kafka主题属性Map-例如,spring.cloud.stream.kafka.bindings.input.consumer.topic.properties.message.format.version=0.9.0.0

          默认值:无

topic.replicas-assignment

          replicas assignments的Map<Integer, List<Integer>>,键为分区,值为分配。 在配置新主题时使用。 请参阅kafka-clients jar中的NewTopic Javadocs。

          默认值:无

topic.replication-factor

          设置主题时要使用的复制因子。覆盖binder-wide的设置。如果存在replicas-assignments,则忽略。

          默认值:无(使用binder-wide的默认值1)

pollTimeout

          用于在可轮询使用者中进行轮询的超时

          默认值:5秒

1.3.3.Consuming Batches

从3.0版开始,当spring.cloud.stream.binding.<name>.consumer.batch-mode设置为true时,通过轮询Kafka Consumer收到的所有记录将作为List<?>呈现给侦听器方法。 否则,该方法将一次调用一个记录。 批处理的大小由Kafka使用者属性max.poll.records,min.fetch.bytes,fetch.max.wait.ms控制; 有关更多信息,请参阅Kafka文档。

请记住,@StreamListener不支持批处理模式-它仅适用于较新的功能编程模型。

使用批处理模式时,不支持在binder中重试,因此maxAttempts将被覆盖为1。您可以配置SeekToCurrentBatchErrorHandler(使用ListenerContainerCustomizer)以实现在binder 中重试的类似功能。 您还可以使用手动AckMode并调用Ackowledgment.nack(index, sleep)来提交部分批处理的偏移量,并重新发送其余记录。 有关这些技术的更多信息,请参阅Spring for Apache Kafka documentation

 

1.3.4.Kafka Producer属性

为了避免重复,Spring Cloud Stream支持以spring.cloud.stream.kafka.default.producer.<property>=<value>的格式为所有通道设置值。

以下属性仅适用于Kafka生产者,并且必须使用前缀

spring.cloud.stream.kafka.bindings.<channelName>.producer.

 

admin.configuration

          从2.1.1版开始,不推荐使用此属性,而推荐使用topic.properties,并且在将来的版本中将不再支持该属性。

admin.replicas-assignment

          从2.1.1版开始,不推荐使用此属性,而推荐使用topic.replicas-assignment,并且在将来的版本中将删除对此属性的支持。

admin.replication-factor

          从2.1.1版开始,不推荐使用此属性,而推荐使用topic.replication-factor,并且在以后的版本中将不再支持该属性。

bufferSize

          Kafka生产者在发送前尝试分批处理的数据量的上限(以字节为单位)

          默认值:16384

sync

          生产者是否同步

          默认值:false

sendTimeoutExpression

          启用同步发布时,将根据传出消息对SpEL表达式进行评估,该传出消息用于评估等待确认的时间(例如headers['mySendTimeout'])。 超时值以毫秒为单位。 在3.0之前的版本中,除非使用本机编码,否则无法使用有效负载,因为在评估此表达式时,有效负载已经是byte[]的形式。 现在,在转换有效负载之前先对表达式求值。

          默认值:无

batchTimeout

          生产者在发送消息之前等待允许更多消息在同一批中累积的时间。 (通常,生产者根本不等待,仅发送在上一次发送过程中累积的所有消息。)非零值可能会增加吞吐量,但会增加延迟。

          默认值:0

messageKeyExpression

          根据用于填充产生的Kafka消息密钥的传出消息评估SpEL表达式,例如headers ['myKey']。 在3.0之前的版本中,除非使用本机编码,否则无法使用有效负载,因为在评估此表达式时,有效负载已经是byte[]的形式。 现在,在转换有效负载之前先对表达式求值。

          默认值:无

headerPatterns

          以逗号分隔的简单模式列表,以匹配要映射到ProducerRecord中的Kafka Headers的Spring messaging headers。 模式可以以通配符(星号)开头或结尾。 可以通过在 前面加上前缀来否定模式。 比赛在第一个比赛(正数或负数)之后停止。 例如 !ask,as* 将传递ash 但 ask.id 和timestamp永远不会映射。

          默认值:*(所有标头 id 和timestamp除外)

configuration

          使用包含通用Kafka生产者属性的键/值对进行映射。

          默认值:Empty map

topic.properties

          设置新主题时使用的Kafka主题属性Map-例如,spring.cloud.stream.kafka.bindings.output.producer.topic.properties.message.format.version=0.9.0.0

topic.replicas-assignment

          replicas assignments的Map<Integer, List<Integer>>,键为分区,值为分配。 在配置新主题时使用。 请参阅kafka-clients jar中的NewTopic Javadocs。

          默认值:无

topic.replication-factor

          设置主题时要使用的复制因子。覆盖binder-wide的设置。如果存在replicas-assignments,则忽略。

          默认值:无(使用binder-wide的默认值1)

useTopicHeader

          设置为true可以用出站邮件中的KafkaHeaders.TOPIC邮件标题的值覆盖默认的binding目标(主题名称)。 如果标题不存在,则使用默认的binding目标。

          默认值:false

recordMetadataChannel

          成功发送结果应发送到的MessageChannel的bean名称; Bean必须存在于应用程序上下文中。 发送到通道的消息是带有附加标头KafkaHeaders.RECORD_METADATA的已发送消息(转换后,如果有的话)。 标头包含由Kafka客户端提供的RecordMetadata对象; 它包括在主题中写入记录的分区和偏移量。

ResultMetadata meta = sendResultMsg.getHeaders().get(KafkaHeaders.RECORD_METADATA, RecordMetadata.class)

发送失败会转到生产者错误通道(如果已配置); 请参阅 Error Channels

          默认值:空

Kafka binder使用生产者的partitionCount设置作为提示来创建具有给定分区数的主题(与minPartitionCount结合使用,minPartitionCount最多为所使用的值)。 同时为绑定程序配置minPartitionCount和为应用程序配置partitionCount时要小心,因为使用了较大的值。 如果已经存在一个分区数较小的主题,并且禁用了autoAddPartitions(默认设置),则绑定器无法启动。 如果已经存在一个分区数较小的主题,并且启用了autoAddPartitions,则会添加新的分区。 如果已经存在的主题的分区数量大于最大数量(minPartitionCount或partitionCount),则使用现有分区数

compression

          设置compression.type生产者属性。 支持的值是none,gzip,snappy和lz4。 如果您将kafka-clients jar覆盖为2.1.0(或更高版本),如Spring for Apache Kafka documentation中所述,并且希望使用zstd压缩,请使用spring.cloud.stream.kafka.bindings.<binding-name>.producer.configuration.compression.type=zstd。

          默认值:无

closeTimeout

          关闭生产者时等待的超时时间(以秒为单位)。

          默认值:30

1.3.5.用法示例

在本节中,我们将说明针对特定方案使用前面的属性。

示例:将autoCommitOffset设置为false并依靠手动打包

此示例说明了如何在消费者应用程序中手动确认偏移。

此示例要求将spring.cloud.stream.kafka.bindings.input.consumer.autoCommitOffset设置为false。 在您的示例中使用相应的输入通道名称。

@SpringBootApplication
@EnableBinding(Sink.class)
public class ManuallyAcknowdledgingConsumer {

 public static void main(String[] args) {
     SpringApplication.run(ManuallyAcknowdledgingConsumer.class, args);
 }

 @StreamListener(Sink.INPUT)
 public void process(Message<?> message) {
     Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
     if (acknowledgment != null) {
         System.out.println("Acknowledgment provided");
         acknowledgment.acknowledge();
     }
 }
}

示例:安全配置

Apache Kafka 0.9支持客户端和代理之间的安全连接。 要利用此功能,请遵循 Apache Kafka Documentation中的准则以及security guidelines from the Confluent documentation。 使用spring.cloud.stream.kafka.binder.configuration选项为binder创建的所有客户端设置安全属性。

例如,要将security.protocol设置为SASL_SSL,请设置以下属性:

spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_SSL

可以以类似方式设置所有其他安全属性

使用Kerberos时,请遵循 reference documentation中的说明来创建和引用JAAS配置。

Spring Cloud Stream支持通过使用JAAS配置文件和Spring Boot属性将JAAS配置信息传递给应用程序。

使用JAAS配置文件

可以使用系统属性为Spring Cloud Stream应用程序设置JAAS和(可选)krb5文件位置。 以下示例显示如何通过使用JAAS配置文件使用SASL和Kerberos启动Spring Cloud Stream应用程序

java -Djava.security.auth.login.config=/path.to/kafka_client_jaas.conf -jar log.jar \
   --spring.cloud.stream.kafka.binder.brokers=secure.server:9092 \
   --spring.cloud.stream.bindings.input.destination=stream.ticktock \
   --spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT

使用Spring Boot属性

作为使用JAAS配置文件的替代方法,Spring Cloud Stream提供了一种通过使用Spring Boot属性为Spring Cloud Stream应用程序设置JAAS配置的机制。

以下属性可用于配置Kafka客户端的登录上下文:

spring.cloud.stream.kafka.binder.jaas.loginModule

          登录模块名称。 正常情况下无需设置

          默认值:com.sun.security.auth.module.Krb5LoginModule

spring.cloud.stream.kafka.binder.jaas.controlFlag

          登录模块的控制标志

          默认值:必填

spring.cloud.stream.kafka.binder.jaas.options

          使用包含登录模块选项的键/值对进行映射

          默认值:Empty map

以下示例显示如何通过使用Spring Boot配置属性使用SASL和Kerberos启动Spring Cloud Stream应用程序

java --spring.cloud.stream.kafka.binder.brokers=secure.server:9092 \
   --spring.cloud.stream.bindings.input.destination=stream.ticktock \
   --spring.cloud.stream.kafka.binder.autoCreateTopics=false \
   --spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT \
   --spring.cloud.stream.kafka.binder.jaas.options.useKeyTab=true \
   --spring.cloud.stream.kafka.binder.jaas.options.storeKey=true \
   --spring.cloud.stream.kafka.binder.jaas.options.keyTab=/etc/security/keytabs/kafka_client.keytab \
   --spring.cloud.stream.kafka.binder.jaas.options.principal=kafka-client-1@EXAMPLE.COM

前面的示例等效于以下JAAS文件:

KafkaClient {
    com.sun.security.auth.module.Krb5LoginModule required
    useKeyTab=true
    storeKey=true
    keyTab="/etc/security/keytabs/kafka_client.keytab"
    principal="kafka-client-1@EXAMPLE.COM";
};

如果所需的主题已经存在于代理上或将由管理员创建,则可以关闭自动创建,仅需要发送客户端JAAS属性

不要在同一应用程序中混合使用JAAS配置文件和Spring Boot属性。 如果-Djava.security.auth.login.config系统属性已经存在,则Spring Cloud Stream将忽略Spring Boot属性

将autoCreateTopics和autoAddPartitions与Kerberos一起使用时要小心。 通常,应用程序可能使用在Kafka和Zookeeper中没有管理权限的主体。 因此,依靠Spring Cloud Stream创建/修改主题可能会失败。 在安全的环境中,强烈建议您使用Kafka Tool 创建主题并以管理方式管理ACL

示例:暂停和恢复消费者

如果希望暂停使用但不引起分区重新平衡,则可以暂停并恢复使用方。 通过将Consumer作为参数添加到@StreamListener中,可以简化此操作。 要恢复,您需要一个用于ListenerContainerIdleEvent实例的ApplicationListener。 事件的发布频率由idleEventInterval属性控制。 由于消费者不是线程安全的,因此必须在调用线程上调用这些方法。

以下简单的应用程序显示了如何暂停和继续

@SpringBootApplication
@EnableBinding(Sink.class)
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

	@StreamListener(Sink.INPUT)
	public void in(String in, @Header(KafkaHeaders.CONSUMER) Consumer<?, ?> consumer) {
		System.out.println(in);
		consumer.pause(Collections.singleton(new TopicPartition("myTopic", 0)));
	}

	@Bean
	public ApplicationListener<ListenerContainerIdleEvent> idleListener() {
		return event -> {
			System.out.println(event);
			if (event.getConsumer().paused().size() > 0) {
				event.getConsumer().resume(event.getConsumer().paused());
			}
		};
	}

}

1.4.Transactional Binder

通过将spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix设置为非空值来启用事务,例如 tx-。 当在处理器应用程序中使用时,消费者启动事务。 在消费者线程上发送的任何记录都参与同一事务。 当侦听器正常退出时,侦听器容器会将偏移量发送到事务并提交。 通用生产者工厂用于使用spring.cloud.stream.kafka.binder.transaction.producer.*属性配置的所有生产者绑定(binding); 单个绑定(binding)Kafka生产者属性将被忽略

事务不支持普通的binder重试(和死信),因为重试将在原始事务中运行,原始事务可能会被回滚,并且任何已发布的记录也会被回滚。 启用重试后(公共属性maxAttempts大于零),重试属性用于配置DefaultAfterRollbackProcessor以在容器级别启用重试。 类似地,此功能不是通过在事务中发布死信记录,而是通过再次在主事务回滚后运行的DefaultAfterRollbackProcessor将该功能移至侦听器容器。

如果您希望在源应用程序中使用事务,或者在某个任意线程中使用事务进行纯生产者事务(例如@Scheduled方法),则必须获取对事务生产者工厂的引用,并使用它定义一个KafkaTransactionManager的bean。

@Bean
public PlatformTransactionManager transactionManager(BinderFactory binders) {
    ProducerFactory<byte[], byte[]> pf = ((KafkaMessageChannelBinder) binders.getBinder(null,
            MessageChannel.class)).getTransactionalProducerFactory();
    return new KafkaTransactionManager<>(pf);
}

注意,我们使用BinderFactory获得了对binder的引用。 如果仅配置了一个binder程序,请在第一个参数中使用null。 如果配置了多个binder,请使用binder名称获取引用。 一旦有了对binder的引用,就可以获取对ProducerFactory的引用并创建一个事务管理器。

然后,您将使用常规的Spring事务支持,例如 TransactionTemplate@Transactional,例如:

public static class Sender {

    @Transactional
    public void doInTransaction(MessageChannel output, List<String> stuffToSend) {
        stuffToSend.forEach(stuff -> output.send(new GenericMessage<>(stuff)));
    }

}

如果您希望将仅生产者事务与其他事务管理器中的事务进行同步,请使用ChainedTransactionManager

1.5.错误通道(Error Channels)

从版本1.3开始,binder程序无条件地将异常发送到每个使用者目的地的错误通道,也可以将其配置为将异步生产者发送失败消息发送到错误通道。 有关更多信息,请参见 [spring-cloud-stream-overview-error-handling]

发送失败的ErrorMessage 的有效负载是具有KafkaSendFailureException的属性:

  • failedMessage: 无法发送的 Spring Messaging Message<?>
  • record: 从failedMessage创建的原始ProducerRecord

没有对生产者异常的自动处理(例如发送到Dead-Letter队列)。 您可以通过自己的Spring Integration流使用这些异常

1.6.(Kafka指标)Kafka Metrics

Kafka binder 模块公开以下指标:

spring.cloud.stream.binder.kafka.offset:此指标指示给定的消费群体尚未从给定的binder主题中消费多少消息。 提供的指标基于Mircometer指标库。 度量标准包含消费者组信息,主题以及与主题上的最新偏移量有关的承诺偏移量的实际滞后时间。 该指标对于向PaaS平台提供自动缩放反馈特别有用

1.7.逻辑删除记录(空记录值)Tombstone Records (null record values)

使用压缩主题时,具有空值的记录(也称为逻辑删除记录)表示键的删除。 要在@StreamListener方法中接收此类消息,必须将参数标记为不需要以接收空值参数。

@StreamListener(Sink.INPUT)
public void in(@Header(KafkaHeaders.RECEIVED_MESSAGE_KEY) byte[] key,
               @Payload(required = false) Customer customer) {
    // customer is null if a tombstone record
    ...
}

1.8.使用KafkaRebalanceListener

当最初分配分区时,应用程序可能希望将主题/分区(topics/partitions)寻找到任意偏移量,或者对消费者执行其他操作。 从2.1版开始,如果在应用程序上下文中提供单个KafkaRebalanceListener的bean,它将连接到所有Kafka消费者binding中

public interface KafkaBindingRebalanceListener {

	/**
	 * Invoked by the container before any pending offsets are committed.
	 * @param bindingName the name of the binding.
	 * @param consumer the consumer.
	 * @param partitions the partitions.
	 */
	default void onPartitionsRevokedBeforeCommit(String bindingName, Consumer<?, ?> consumer,
			Collection<TopicPartition> partitions) {

	}

	/**
	 * Invoked by the container after any pending offsets are committed.
	 * @param bindingName the name of the binding.
	 * @param consumer the consumer.
	 * @param partitions the partitions.
	 */
	default void onPartitionsRevokedAfterCommit(String bindingName, Consumer<?, ?> consumer, Collection<TopicPartition> partitions) {

	}

	/**
	 * Invoked when partitions are initially assigned or after a rebalance.
	 * Applications might only want to perform seek operations on an initial assignment.
	 * @param bindingName the name of the binding.
	 * @param consumer the consumer.
	 * @param partitions the partitions.
	 * @param initial true if this is the initial assignment.
	 */
	default void onPartitionsAssigned(String bindingName, Consumer<?, ?> consumer, Collection<TopicPartition> partitions,
			boolean initial) {

	}

}

提供重新平衡侦听器时,不能将resetOffsets消费者属性设置为true

1.9.死信主题处理 Dead-Letter Topic Processing

1.9.1.死信主题分区选择 Dead-Letter Topic Partition Selection

默认情况下,记录将使用与原始记录相同的分区发布到Dead-Letter主题。 这意味着Dead-Letter主题必须具有至少与原始记录一样多的分区。

若要更改此行为,请将DlqPartitionFunction实现作为@Bean添加到应用程序上下文中。 只能存在一个这样的bean。 消费者组,失败的ConsumerRecord和异常提供了该功能。 例如,如果您始终要路由到分区0,则可以使用

@Bean
public DlqPartitionFunction partitionFunction() {
    return (group, record, ex) -> 0;
}

如果将消费者绑定的(binding’s)dlqPartitions属性设置为1(并且绑定器的( binder’s)minPartitionCount等于1),则无需提供DlqPartitionFunction; 框架将始终使用分区0。如果将消费者绑定的(binding’s)dlqPartitions属性设置为大于1的值(或者绑定器的( binder’s)minPartitionCount大于1),则即使分区计数与原始主题的计数相同,也必须提供DlqPartitionFunction Bean。。

1.9.2.处理死信主题中的记录 Handling Records in a Dead-Letter Topic

因为该框架无法预期用户将如何处置死信,所以它没有提供任何标准机制来处理它们。 如果死信的原因是短暂的,则您可能希望将消息路由回原始主题。 但是,如果问题是永久性问题,则可能导致无限循环。 本主题中的示例Spring Boot应用程序是如何将这些消息路由回原始主题的示例,但是在尝试了三遍之后,它将它们移动到了“停车场(parking lot)”主题。 该应用程序是另一个从死信主题中读取的spring-cloud-stream应用程序。 5秒钟未收到任何消息时,它将终止。

这些示例假定原始目的地为so8400out,而消费者组为so8400。

有两种策略可供考虑

  • 考虑仅在主应用程序未运行时才运行重新路由。 否则,瞬态错误的重试会很快用完。
  • 使用两阶段方法:使用此应用程序可以路由到第三个主题,而另一个应用程序可以从那里路由回到主要主题。

以下代码清单显示了示例应用程序:

application.properties

spring.cloud.stream.bindings.input.group=so8400replay
spring.cloud.stream.bindings.input.destination=error.so8400out.so8400

spring.cloud.stream.bindings.output.destination=so8400out

spring.cloud.stream.bindings.parkingLot.destination=so8400in.parkingLot

spring.cloud.stream.kafka.binder.configuration.auto.offset.reset=earliest

spring.cloud.stream.kafka.binder.headers=x-retries

Application

@SpringBootApplication
@EnableBinding(TwoOutputProcessor.class)
public class ReRouteDlqKApplication implements CommandLineRunner {

    private static final String X_RETRIES_HEADER = "x-retries";

    public static void main(String[] args) {
        SpringApplication.run(ReRouteDlqKApplication.class, args).close();
    }

    private final AtomicInteger processed = new AtomicInteger();

    @Autowired
    private MessageChannel parkingLot;

    @StreamListener(Processor.INPUT)
    @SendTo(Processor.OUTPUT)
    public Message<?> reRoute(Message<?> failed) {
        processed.incrementAndGet();
        Integer retries = failed.getHeaders().get(X_RETRIES_HEADER, Integer.class);
        if (retries == null) {
            System.out.println("First retry for " + failed);
            return MessageBuilder.fromMessage(failed)
                    .setHeader(X_RETRIES_HEADER, new Integer(1))
                    .setHeader(BinderHeaders.PARTITION_OVERRIDE,
                            failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
                    .build();
        }
        else if (retries.intValue() < 3) {
            System.out.println("Another retry for " + failed);
            return MessageBuilder.fromMessage(failed)
                    .setHeader(X_RETRIES_HEADER, new Integer(retries.intValue() + 1))
                    .setHeader(BinderHeaders.PARTITION_OVERRIDE,
                            failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
                    .build();
        }
        else {
            System.out.println("Retries exhausted for " + failed);
            parkingLot.send(MessageBuilder.fromMessage(failed)
                    .setHeader(BinderHeaders.PARTITION_OVERRIDE,
                            failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
                    .build());
        }
        return null;
    }

    @Override
    public void run(String... args) throws Exception {
        while (true) {
            int count = this.processed.get();
            Thread.sleep(5000);
            if (count == this.processed.get()) {
                System.out.println("Idle, terminating");
                return;
            }
        }
    }

    public interface TwoOutputProcessor extends Processor {

        @Output("parkingLot")
        MessageChannel parkingLot();

    }

}

1.10.使用Kafka Binder进行分区

Apache Kafka本机支持主题分区。

有时,将数据发送到特定的分区是有好处的,例如,当您要严格订购消息处理时(特定客户的所有消息都应转到同一分区)。

以下示例显示了如何配置生产者和消费者端:

@SpringBootApplication
@EnableBinding(Source.class)
public class KafkaPartitionProducerApplication {

    private static final Random RANDOM = new Random(System.currentTimeMillis());

    private static final String[] data = new String[] {
            "foo1", "bar1", "qux1",
            "foo2", "bar2", "qux2",
            "foo3", "bar3", "qux3",
            "foo4", "bar4", "qux4",
            };

    public static void main(String[] args) {
        new SpringApplicationBuilder(KafkaPartitionProducerApplication.class)
            .web(false)
            .run(args);
    }

    @InboundChannelAdapter(channel = Source.OUTPUT, poller = @Poller(fixedRate = "5000"))
    public Message<?> generate() {
        String value = data[RANDOM.nextInt(data.length)];
        System.out.println("Sending: " + value);
        return MessageBuilder.withPayload(value)
                .setHeader("partitionKey", value)
                .build();
    }

}

application.yml

spring:
  cloud:
    stream:
      bindings:
        output:
          destination: partitioned.topic
          producer:
            partition-key-expression: headers['partitionKey']
            partition-count: 12

必须为该主题提供足够的分区,以实现所有消费者组所需的并发性。 上面的配置最多支持12个使用者实例(如果它们的并发是2,则为6;如果它们的并发是3,则为4,依此类推)。 通常最好是“过度供应”分区,以允许将来增加消费者或并发性。

前面的配置使用默认分区(key.hashCode() % partitionCount)。 根据密钥值,这可能会或可能不会提供适当的平衡算法。 您可以使用partitionSelectorExpression或partitionSelectorClass属性覆盖此默认值。

由于分区是由Kafka本地处理的,因此在用户端不需要特殊配置。 Kafka在实例之间分配分区。

以下Spring Boot应用程序侦听Kafka流并打印(到控制台)每条消息去往的分区ID:

@SpringBootApplication
@EnableBinding(Sink.class)
public class KafkaPartitionConsumerApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(KafkaPartitionConsumerApplication.class)
            .web(false)
            .run(args);
    }

    @StreamListener(Sink.INPUT)
    public void listen(@Payload String in, @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
        System.out.println(in + " received from partition " + partition);
    }

}

application.yml

spring:
  cloud:
    stream:
      bindings:
        input:
          destination: partitioned.topic
          group: myGroup

您可以根据需要添加实例。 Kafka重新平衡分区分配。 如果实例计数(或实例计数*并发性)超过分区数,则某些使用者处于空闲状态。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值