问题一:
背景:使用pt文件生成的java
报错信息:
尝试序列化或反序列化 Protos.AdvertChain
对象时,Flink 使用的 Kryo 序列化器遇到了一个未注册的类 ID。Kryo 是一种高效的 Java 序列化库,默认情况下它不会自动注册所有的类。如果你的应用程序中使用了某些特定的类
解决办法:
- 找到所有需要注册的类:检查你的 Protobuf 定义文件,并找出所有可能包含在
Protos.AdvertChain
或者其他相关对象中的消息类型。 - 注册这些类到 Kryo:在 Flink 的配置环境中添加对这些类的支持
// 不可变集合等特殊类型,注册对应的序列化器
try {
//注册 Protobuf 类型的序列化器
env.getConfig().registerTypeWithKryoSerializer(Protos.FlowrateHumanProperty.class, ProtobufSerializer.class);
env.getConfig().registerTypeWithKryoSerializer(Protos.AdvertPlaySnap.class, ProtobufSerializer.class);
env.getConfig().registerTypeWithKryoSerializer(Protos.AdvertChain.class, ProtobufSerializer.class);
// 注册 UnmodifiableCollection 序列化器
Class<?> unmodColl = Class.forName("java.util.Collections$UnmodifiableCollection");
env.getConfig().addDefaultKryoSerializer(unmodColl, UnmodifiableCollectionsSerializer.class);
// 注册 UnmodifiableList 序列化器
Class<?> unmodList = Class.forName("java.util.Collections$UnmodifiableList");
env.getConfig().addDefaultKryoSerializer(unmodList, UnmodifiableCollectionsSerializer.class);
// 注册 UnmodifiableSet 序列化器
Class<?> unmodSet = Class.forName("java.util.Collections$UnmodifiableSet");
env.getConfig().addDefaultKryoSerializer(unmodSet, UnmodifiableCollectionsSerializer.class);
// 注册 UnmodifiableMap 序列化器
Class<?> unmodMap = Class.forName("java.util.Collections$UnmodifiableMap");
env.getConfig().addDefaultKryoSerializer(unmodMap, UnmodifiableCollectionsSerializer.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
问题二:
报错:不可序列化
Exception in thread "main" org.apache.flink.api.common.InvalidProgramException: The implementation of the KeySelector is not serializable. The implementation accesses fields of its enclosing class, which is a common reason for non-serializability. A common solution is to make the function a proper (non-inner) class, or a static inner class.
at org.apache.flink.api.java.ClosureCleaner.clean(ClosureCleaner.java:164)
at org.apache.flink.api.java.ClosureCleaner.clean(ClosureCleaner.java:69)
at org.apache.flink.streaming.api.environment.StreamExecutionEnvironment.clean(StreamExecutionEnvironment.java:2317)
at org.apache.flink.streaming.api.datastream.DataStream.clean(DataStream.java:202)
at org.apache.flink.streaming.api.datastream.DataStream.keyBy(DataStream.java:292)
at com.xzl.bigdata.realtime.dwd.advertis.tpAdvSnapV2.app.DwdTpAdvSnpV2.getKeyBySnpv2(DwdTpAdvSnpV2.java:192)
at com.xzl.bigdata.realtime.dwd.advertis.tpAdvSnapV2.app.DwdTpAdvSnpV2.handle(DwdTpAdvSnpV2.java:63)
at com.xzl.bigdata.realtime.common.app.BaseAPP_test.start(BaseAPP_test.java:53)
at com.xzl.bigdata.realtime.dwd.advertis.tpAdvSnapV2.app.DwdTpAdvSnpV2.main(DwdTpAdvSnpV2.java:45)
Caused by: java.io.NotSerializableException: com.xzl.bigdata.realtime.dwd.advertis.tpAdvSnapV2.app.DwdTpAdvSnpV2
原因
- 避免了对外部实例的隐式引用:在原来的实现中,匿名内部类(或非静态内部类)会隐式地持有对外部类实例的引用。如果外部类包含未实现
Serializable
接口的成员变量,那么整个对象图就无法序列化。通过将其改为静态内部类或者独立类,你消除了这种隐式的对外部类实例的引用,从而确保了KeySelector
自身是可序列化的。 - 简化了依赖关系:静态内部类不持有对外部类实例的引用,因此它们不会试图序列化外部类的状态。这使得 Flink 能够更容易地将函数发送到集群中的各个节点执行。
- Flink 需要将函数(如 KeySelector、MapFunction 等)发送到集群中的各个节点执行,因此这些函数必须是可序列化的。当你的 KeySelector 实现类内部访问了外部类的非静态字段或方法时,会导致该实现类不可序列化,因为默认情况下,它会隐式地持有对外部类实例的引用。
解决:
- 实现
Serializable
接口 - 方法改为静态