Spring Boot和Apache Kafka結合實現錯誤處理,訊息轉換和事務支援?
Spring為Kafka帶來了熟悉的Spring程式設計模型。它提供了KafkaTemplate用於釋出記錄和用於非同步執行POJO偵聽器的偵聽器容器。Spring Boot自動配置連線了大部分基礎架構,因此您可以專注於業務邏輯。
錯誤恢復
考慮這個簡單的POJO偵聽器方法:
@KafkaListener(id = "fooGroup", topics = "topic1") public void listen(String in) { logger.info("Received: " + in); if (in.startsWith("foo")) { throw new RuntimeException("failed"); } } |
預設情況下,只記錄失敗的記錄,然後我們繼續下一個記錄。但是,我們可以在偵聽器容器中配置錯誤處理程式以執行其他操作。為此,我們使用我們自己的方法覆蓋Spring Boot的自動配置容器工廠:
@Bean public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory( ConcurrentKafkaListenerContainerFactoryConfigurer configurer, ConsumerFactory<Object, Object> kafkaConsumerFactory) { ConcurrentKafkaListenerContainerFactory<Object, Object> factory = new ConcurrentKafkaListenerContainerFactory<>(); configurer.configure(factory, kafkaConsumerFactory); factory.setErrorHandler(new SeekToCurrentErrorHandler()); // <<<<<< return factory; } |
請注意,我們仍然可以利用大部分自動配置。
SeekToCurrentErrorHandler丟棄來自poll()剩下的記錄,並執行seek操作實現消費者操作偏移offset復位,使丟棄記錄在下一次輪詢再取出。預設情況下,錯誤處理程式會跟蹤失敗的記錄,在10次傳遞嘗試後放棄並記錄失敗的記錄。但是,我們也可以將失敗的訊息傳送到另一個主題。我們稱之為死信主題。
下面是合在一起程式碼:
@Bean public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory( ConcurrentKafkaListenerContainerFactoryConfigurer configurer, ConsumerFactory<Object, Object> kafkaConsumerFactory, KafkaTemplate<Object, Object> template) { ConcurrentKafkaListenerContainerFactory<Object, Object> factory = new ConcurrentKafkaListenerContainerFactory<>(); configurer.configure(factory, kafkaConsumerFactory); factory.setErrorHandler(new SeekToCurrentErrorHandler( new DeadLetterPublishingRecoverer(template), 3)); return factory; } @KafkaListener(id = "fooGroup", topics = "topic1") public void listen(String in) { logger.info("Received: " + in); if (in.startsWith("foo")) { throw new RuntimeException("failed"); } } @KafkaListener(id = "dltGroup", topics = "topic1.DLT") public void dltListen(String in) { logger.info("Received from DLT: " + in); } |
反序列化錯誤
但是,在Spring獲得記錄之前發生的反序列化異常呢?使用ErrorHandlingDeserializer。此反序列化器包裝委託反序列化器並捕獲任何異常。然後將它們轉發到偵聽器容器,該容器將它們直接傳送到錯誤處理程式。該異常包含源資料,因此您可以診斷問題。
領域物件並推斷型別
請考慮以下示例:
@Bean public RecordMessageConverter converter() { return new StringJsonMessageConverter(); } @KafkaListener(id = "fooGroup", topics = "topic1") public void listen(Foo2 foo) { logger.info("Received: " + foo); if (foo.getFoo().startsWith("fail")) { throw new RuntimeException("failed"); } } @KafkaListener(id = "dltGroup", topics = "topic1.DLT") public void dltListen(Foo2 in) { logger.info("Received from DLT: " + in); } |
請注意,我們現在正在使用型別的物件Foo2。訊息轉換器bean推斷要轉換為方法簽名中的引數型別的型別。轉換器自動“信任”該型別。Spring Boot自動將轉換器配置到偵聽器容器中。
在生產者方面,傳送的物件可以是不同的類(只要它是型別相容的):
@RestController public class Controller { @Autowired private KafkaTemplate<Object, Object> template; @PostMapping(path = "/send/foo/{what}") public void sendFoo(@PathVariable String what) { this.template.send("topic1", new Foo1(what)); } } |
配置:
spring: kafka: producer: value-serializer: org.springframework.kafka.support.serializer.JsonSerializer $ curl -X POST http://localhost:8080/send/foo/fail |
在這裡,我們使用StringDeserializer,並在消費者端使用一個“智慧”訊息轉換器。
多方法監聽器
我們還可以使用單個偵聽器容器並根據型別路由到特定方法。由於有多個方法,型別需要選擇要呼叫的方法,因此這裡我們就無法推斷型別了。
相反,我們依賴於記錄頭中傳遞的型別資訊來從源型別對映到目標型別。此外,由於我們不推斷型別,我們需要配置訊息轉換器以“信任”包的對映型別。
在這種情況下,我們將在兩側使用訊息轉換器( StringSerializer和StringDeserializer 一起使用)。以下消費者側轉換器示例將它們放在一起:
@Bean public RecordMessageConverter converter() { StringJsonMessageConverter converter = new StringJsonMessageConverter(); DefaultJackson2JavaTypeMapper typeMapper = new DefaultJackson2JavaTypeMapper(); typeMapper.setTypePrecedence(TypePrecedence.TYPE_ID); typeMapper.addTrustedPackages("com.common"); Map<String, Class<?>> mappings = new HashMap<>(); mappings.put("foo", Foo2.class); mappings.put("bar", Bar2.class); typeMapper.setIdClassMapping(mappings); converter.setTypeMapper(typeMapper); return converter; } |
在這裡,我們將“foo”對映到類Foo2,將“bar” 對映到類Bar2。請注意,我們必須告訴它使用TYPE_ID標頭來確定轉換的型別。同樣,Spring Boot會自動將訊息轉換器配置到容器中。下面是application.yml檔案片段中的生產者端型別對映; 格式是以冒號分隔的token:FQCN列表:
spring: kafka: producer: value-serializer: org.springframework.kafka.support.serializer.JsonSerializer properties: spring.json.type.mapping: foo:com.common.Foo1,bar:com.common.Bar1 |
此配置將類對映Foo1到“foo”,將類對映Bar1到“bar”。
監聽器:
@Component @KafkaListener(id = "multiGroup", topics = { "foos", "bars" }) public class MultiMethods { @KafkaHandler public void foo(Foo1 foo) { System.out.println("Received: " + foo); } @KafkaHandler public void bar(Bar bar) { System.out.println("Received: " + bar); } @KafkaHandler(isDefault = true) public void unknown(Object object) { System.out.println("Received unknown: " + object); } } |
生產者:
@RestController public class Controller { @Autowired private KafkaTemplate<Object, Object> template; @PostMapping(path = "/send/foo/{what}") public void sendFoo(@PathVariable String what) { this.template.send(new GenericMessage<>(new Foo1(what), Collections.singletonMap(KafkaHeaders.TOPIC, "foos"))); } @PostMapping(path = "/send/bar/{what}") public void sendBar(@PathVariable String what) { this.template.send(new GenericMessage<>(new Bar(what), Collections.singletonMap(KafkaHeaders.TOPIC, "bars"))); } @PostMapping(path = "/send/unknown/{what}") public void sendUnknown(@PathVariable String what) { this.template.send(new GenericMessage<>(what, Collections.singletonMap(KafkaHeaders.TOPIC, "bars"))); } } |
事務
透過在application.yml檔案中設定transactional-id-prefix來啟用事務:
spring: kafka: producer: value-serializer: org.springframework.kafka.support.serializer.JsonSerializer transaction-id-prefix: tx. consumer: properties: isolation.level: read_committed |
當使用spring-kafka 1.3.x或更高版本以及支援事務的kafka-clients版本(0.11或更高版本)時,方法中KafkaTemplate執行的任何操作@KafkaListener都將參與事務,並且偵聽器容器將在提交之前將偏移傳送到事務它。認識到我們還為消費者設定了隔離級別,使其無法檢視未提交的記錄。以下示例暫停偵聽器,以便我們可以看到此效果:
@KafkaListener(id = "fooGroup2", topics = "topic2") public void listen(List foos) throws IOException { logger.info("Received: " + foos); foos.forEach(f -> kafkaTemplate.send("topic3", f.getFoo().toUpperCase())); logger.info("Messages sent, hit enter to commit tx"); System.in.read(); } @KafkaListener(id = "fooGroup3", topics = "topic3") public void listen(String in) { logger.info("Received: " + in); } |
此示例的生產者在單個事務中傳送多個記錄:
@PostMapping(path = "/send/foos/{what}") public void sendFoo(@PathVariable String what) { this.template.executeInTransaction(kafkaTemplate -> { StringUtils.commaDelimitedListToSet(what).stream() .map(s -> new Foo1(s)) .forEach(foo -> kafkaTemplate.send("topic2", foo)); return null; }); } curl -X POST http://localhost:8080/send/foos/a,b,c,d,e Received: [Foo2 [foo=a], Foo2 [foo=b], Foo2 [foo=c], Foo2 [foo=d], Foo2 [foo=e]] Messages sent, hit Enter to commit tx Received: [A, B, C, D, E] |
更多Spring Cloud Stream對Kafka的支援點選kafka標籤進入
相關文章
- Apache Kafka和Spring Boot的容錯和可靠訊息傳遞 – Arnold GalovicsApacheKafkaSpring Boot
- 玩轉spring boot——結合JPA事務Spring Boot
- Laravel 實現 Kafka 訊息推送與接收處理LaravelKafka
- spring boot 全域性錯誤處理Spring Boot
- RabbitMQ,RocketMQ,Kafka 事務性,訊息丟失和訊息重複傳送的處理策略MQKafka
- 使用Spring Boot和Kafka Streams實現CQRSSpring BootKafka
- 使用Kafka Streams和Spring Boot微服務中的分散式事務 - PiotrKafkaSpring Boot微服務分散式
- Kafka聯結器深度解讀之錯誤處理和死信佇列Kafka佇列
- Go 語言的錯誤訊息處理Go
- 玩轉spring boot——結合AngularJs和JDBCSpring BootAngularJSJDBC
- 玩轉spring boot——結合jQuery和AngularJsSpring BootjQueryAngularJS
- 使用 Spring Boot 提供API錯誤訊息的好方式Spring BootAPI
- 七、Spring Boot 錯誤處理原理 & 定製錯誤頁面Spring Boot
- 實現宣告式鎖,支援分散式鎖自定義鎖、SpEL和結合事務分散式
- spring boot使用Java並行流傳送kafka訊息報錯Spring BootJava並行Kafka
- 以事務方式傳送 Kafka 訊息Kafka
- 如何處理錯誤訊息PleaseinstalltheLinuxkernelheaderfilesLinuxHeader
- 使用Spring Boot實現訊息佇列Spring Boot佇列
- 使用Spring Boot實現事務管理Spring Boot
- 使用Spring Boot和Kafka Streams實現基於SAGA模式的分散式事務原始碼教程 - PiotrSpring BootKafka模式分散式原始碼
- Spring boot/Spring 統一錯誤處理方案的使用Spring Boot
- PHP錯誤處理和異常處理PHP
- 靈活定義和處理SOAP頭訊息 (轉)
- KafkaConsumer對於事務訊息的處理Kafka
- RocketMQ的事務訊息處理【half-message】MQ
- JEESZ-kafka訊息服務平臺實現Kafka
- Apache Camel與Spring-boot和Kafka的整合開源案例ApacheSpringbootKafka
- 使用Spring Boot實現分散式事務Spring Boot分散式
- Apache Kafka訊息傳遞策略ApacheKafka
- 訊息佇列之事務訊息,RocketMQ 和 Kafka 是如何做的?佇列MQKafka
- 詳談:Redis事務和訊息訂閱Redis
- kafka 副本機制和容錯處理 -2Kafka
- Kafka事務實現原理Kafka
- spring boot 結合Redis 實現工具類Spring BootRedis
- 優步是如何使用Apache Flink和Kafka實現實時Exactly-Once廣告事件處理?ApacheKafka事件
- Spring (二) 事務處理Spring
- 使用Spring Boot實現Redis事務 | VinsguruSpring BootRedis
- Spring boot +mybatis 實現宣告式事務管理Spring BootMyBatis