版本
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="[email protected]";
};
如果所需的主題已經存在於代理上或將由管理員創建,則可以關閉自動創建,僅需要發送客戶端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重新平衡分區分配。 如果實例計數(或實例計數*併發性)超過分區數,則某些使用者處於空閒狀態。