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="[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重新平衡分區分配。 如果實例計數(或實例計數*併發性)超過分區數,則某些使用者處於空閒狀態。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章