![](/img/trans.png)
[英]Can't deserialize data in the Kafka Stream using Spring Cloud Streams
[英]Spring Cloud Kafka: Can't serialize data for output stream when two processors are active
我有一個具有函數式編程風格的 Spring Cloud Kafka Streams 的工作設置。 有兩個用例,通過application.properties
配置。 它們都單獨工作,但是一旦我同時激活它們,我就會收到第二個用例的 output stream 的序列化錯誤:
Exception in thread "ActivitiesAppId-05296224-5ea1-412a-aee4-1165870b5c75-StreamThread-1" org.apache.kafka.streams.errors.StreamsException:
Error encountered sending record to topic outputActivities for task 0_0 due to:
...
Caused by: org.apache.kafka.common.errors.SerializationException:
Can't serialize data [com.example.connector.model.Activity@497b37ff] for topic [outputActivities]
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Incompatible types: declared root type ([simple type, class com.example.connector.model.Material]) vs com.example.connector.model.Activity
這里的最后一行很重要,因為“聲明的根類型”來自Material
class,而不是Activity
class,這可能是源錯誤。
同樣,當我在啟動應用程序之前只激活第二個用例時,一切正常。 所以我假設“材料”處理器以某種方式干擾了“活動”處理器(或其序列化程序),但我不知道何時何地。
設置
1.) 用例:“材料”
@Bean
public Function<KStream<String, MaterialRaw>, KStream<String, Material>> processMaterials() {...}
application.properties
spring.cloud.stream.kafka.streams.binder.functions.processMaterials.applicationId=MaterialsAppId
spring.cloud.stream.bindings.processMaterials-in-0.destination=inputMaterialsRaw
spring.cloud.stream.bindings.processMaterials-out-0.destination=outputMaterials
2.) 用例:“活動”
@Bean
public BiFunction<KStream<String, ActivityRaw>, KStream<String, Assignee>, KStream<String, Activity>> processActivities() {...}
application.properties
spring.cloud.stream.kafka.streams.binder.functions.processActivities.applicationId=ActivitiesAppId
spring.cloud.stream.bindings.processActivities-in-0.destination=inputActivitiesRaw
spring.cloud.stream.bindings.processActivities-in-1.destination=inputAssignees
spring.cloud.stream.bindings.processActivities-out-0.destination=outputActivities
The two processors are also defined as stream function in application.properties
: spring.cloud.stream.function.definition=processActivities;processMaterials
謝謝!
更新 - 這是我在代碼中使用處理器的方式:
執行
// Material model
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class MaterialRaw {
private String id;
private String name;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Material {
private String id;
private String name;
}
// Material processor
@Bean
public Function<KStream<String, MaterialRaw>, KStream<String, Material>> processMaterials() {
return materialsRawStream -> materialsRawStream .map((recordKey, materialRaw) -> {
// some transformation
final var newId = materialRaw.getId() + "---foo";
final var newName = materialRaw.getName() + "---bar";
final var material = new Material(newId, newName);
// output
return new KeyValue<>(recordKey, material);
};
}
// Activity model
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ActivityRaw {
private String id;
private String name;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Assignee {
private String id;
private String assignedAt;
}
/**
* Combination of `ActivityRaw` and `Assignee`
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Activity {
private String id;
private Integer number;
private String assignedAt;
}
// Activity processor
@Bean
public BiFunction<KStream<String, ActivityRaw>, KStream<String, Assignee>, KStream<String, Activity>> processActivities() {
return (activitiesRawStream, assigneesStream) -> {
final var joinWindow = JoinWindows.of(Duration.ofDays(30));
final var streamJoined = StreamJoined.with(
Serdes.String(),
new JsonSerde<>(ActivityRaw.class),
new JsonSerde<>(Assignee.class)
);
final var joinedStream = activitiesRawStream.leftJoin(
assigneesStream,
new ActivityJoiner(),
joinWindow,
streamJoined
);
final var mappedStream = joinedStream.map((recordKey, activity) -> {
return new KeyValue<>(recordKey, activity);
});
return mappedStream;
};
}
當有多個具有不同出站目標類型的函數時,綁定器推斷Serde
類型的方式存在問題,在您的情況下,一個具有Activity
,另一個具有Material
。 我們將不得不在活頁夾中解決這個問題。 我在這里創建了一個問題。
同時,您可以遵循此解決方法。
創建一個自定義Serde
class,如下所示。
public class ActivitySerde extends JsonSerde<Activity> {}
然后,使用配置顯式將此Serde
用於您的processActivities
function 的出站。
例如,
spring.cloud.stream.kafka.streams.bindings.processActivities-out-0.producer.valueSerde=com.example.so65003575.ActivitySerde
如果您嘗試此解決方法,請將 package 更改為適當的。
這是另一種推薦的方法。 如果您使用目標類型定義Serde
類型的 bean,則優先,因為綁定器將與KStream
類型進行匹配。 因此,您也可以在上述解決方法中不定義額外的 class 的情況下執行此操作。
@Bean
public Serde<Activity> activitySerde() {
return new JsonSerde(Activity.class);
}
這是解釋所有這些細節的文檔。
您需要為每個 function s.c.s.bindings.xxx.binder=...
指定要使用的活頁夾。
但是,如果沒有它,我會預料到會出現諸如“找到多個活頁夾但未指定默認值”之類的錯誤,這就是消息通道活頁夾會發生的情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.