簡體   English   中英

Spring Cloud Kafka:當兩個處理器處於活動狀態時,無法序列化 output stream 的數據

[英]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.) 用例:“材料”

  • 一個輸入 stream -> 轉換 -> 一個 output stream
@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.) 用例:“活動”

  • 兩個輸入流 -> 加入 -> 一個 output stream
@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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM