版本
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
仅在设置了autoCreateTopics或autoAddPartitions时有效。 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.id和spring-kafka文档中的Transactions。 启用事务后,单个生产者属性将被忽略,所有生产者都将使用spring.cloud.stream.kafka.binder.transaction.producer.*属性。
默认值: null (无事务)
spring.cloud.stream.kafka.binder.transaction.producer.*
在事务代理(binder)中生产者的全局生产者属性。 请参阅spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix和Kafka 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.instanceCount和spring.cloud.stream.instanceIndex为每个消费者分配固定的一组分区。 这要求在每个启动的实例上都正确设置spring.cloud.stream.instanceCount和spring.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.serializer和dlqProducerProperties.configuration.value.serializer的形式提供。
默认值:默认的Kafka生产者属性
standardHeaders
指示入站通道适配器填充哪些标准头。 允许的值:none
, id
, timestamp
, both
。 如果使用本机反序列化并且第一个组件接收消息需要一个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重新平衡分区分配。 如果实例计数(或实例计数*并发性)超过分区数,则某些使用者处于空闲状态。