负载均衡系统中的 Protobuf-net 动态类型
Protobuf-net dynamic types in a load-balanced system
我在一个应用程序中使用 protobuf-net,该应用程序对来自(出于所有意图和目的)第 3 方 dll 的对象进行大量二进制序列化。因此,我无法在合同本身上使用 [Proto-] 属性,而是使用 RuntimeTypeModel 在 运行 遇到新类型时准备序列化程序。示例序列化程序包装器:
public static class ProtobufSerializer
{
public static byte[] Serialize<T>(T obj)
{
PrepareSerializer(typeof(T));
ProtoBuf.Serialize(memoryStream, obj);
}
public static T Deserialize<T>(byte[] bytes)
{
PrepareSerializer(typeof(T));
ProtoBuf.Serialize(memoryStream, obj);
}
}
我们可以安全地假设 PrepareSerializer
能够准备 RuntimeTypeModel
序列化任何给定的类型。我在对象的反序列化方面遇到了一些问题,但我必须利用 DynamicType=true
。例如,给定以下界面:
public interface IFoo
{
string Name {get;}
}
和实施:
public class Foo : IFoo
{
public string Name {get;set;}
public Bar Bar {get;set;}
[OnDeserializing]
public void OnDeserializing(Type t)
{
PrepareSerializer(typeof(Foo));
}
}
public class Bar
{
public int Baz {get;set;}
}
PrepareSerializer
方法本质上会使用代理并生成大致相当于:
的模型
// registered surrogate for IFoo
[ProtoContract]
public class IFooSurrogate
{
[ProtoMember(1, DynamicType=true)]
public object Value
[OnSerializing]
public void OnSerializing(Type t)
{
PrepareSerializer(this.Value.GetType());
}
}
其中 Value
由隐式转换器设置为等于 IFoo 的实例。这在序列化期间工作正常(事件被触发并让我有机会为特定接口实现类型准备序列化程序)。它在非分布式系统中也能正常工作,在非分布式系统中我必须 运行 通过序列化方法才能尝试反序列化该类型。但是,在分布式系统中的反序列化过程中,当前节点以前从未见过 Foo,ProtoBuf.Serializer 抛出一个 InvalidOperationException
抱怨缺少类型 Bar
之前的序列化程序 运行 是 Foo.OnDeserializing 事件(让我有机会告诉它如何反序列化 Bar)。
在 protobuf-net 抱怨缺少序列化器之前,有什么方法可以附加一个钩子来确保我的代码有机会了解 'Foo'?
我还没有完全尝试过这种情况,但是:为了允许一些灵活性,所有 Type
存储和再水化都通过 TypeModel.DynamicTypeFormatting
事件进行;所以,理论上你 可以 将这个事件挂接到 RuntimeTypeModel.Default
,像这样:
RuntimeTypeModel.DynamicTypeFormatting += (sender, args) => {
if (args.FormattedName != null) { // meaning: rehydrating
lock(SomeSyncLock) {
if(NotYetKnown(args.FormattedName))
Prepare(args.FormattedName);
}
}
};
此 API 的 intent 是为了让您控制类型的解析方式,但是...我想它也适用于此吗?
但是,我可以理解在第一次看到新的 Type
时更有针对性的事件的想法,本质上是替换/补充 "apply default behaviour" 代码。不过,我认为 今天 不存在。
我在一个应用程序中使用 protobuf-net,该应用程序对来自(出于所有意图和目的)第 3 方 dll 的对象进行大量二进制序列化。因此,我无法在合同本身上使用 [Proto-] 属性,而是使用 RuntimeTypeModel 在 运行 遇到新类型时准备序列化程序。示例序列化程序包装器:
public static class ProtobufSerializer
{
public static byte[] Serialize<T>(T obj)
{
PrepareSerializer(typeof(T));
ProtoBuf.Serialize(memoryStream, obj);
}
public static T Deserialize<T>(byte[] bytes)
{
PrepareSerializer(typeof(T));
ProtoBuf.Serialize(memoryStream, obj);
}
}
我们可以安全地假设 PrepareSerializer
能够准备 RuntimeTypeModel
序列化任何给定的类型。我在对象的反序列化方面遇到了一些问题,但我必须利用 DynamicType=true
。例如,给定以下界面:
public interface IFoo
{
string Name {get;}
}
和实施:
public class Foo : IFoo
{
public string Name {get;set;}
public Bar Bar {get;set;}
[OnDeserializing]
public void OnDeserializing(Type t)
{
PrepareSerializer(typeof(Foo));
}
}
public class Bar
{
public int Baz {get;set;}
}
PrepareSerializer
方法本质上会使用代理并生成大致相当于:
// registered surrogate for IFoo
[ProtoContract]
public class IFooSurrogate
{
[ProtoMember(1, DynamicType=true)]
public object Value
[OnSerializing]
public void OnSerializing(Type t)
{
PrepareSerializer(this.Value.GetType());
}
}
其中 Value
由隐式转换器设置为等于 IFoo 的实例。这在序列化期间工作正常(事件被触发并让我有机会为特定接口实现类型准备序列化程序)。它在非分布式系统中也能正常工作,在非分布式系统中我必须 运行 通过序列化方法才能尝试反序列化该类型。但是,在分布式系统中的反序列化过程中,当前节点以前从未见过 Foo,ProtoBuf.Serializer 抛出一个 InvalidOperationException
抱怨缺少类型 Bar
之前的序列化程序 运行 是 Foo.OnDeserializing 事件(让我有机会告诉它如何反序列化 Bar)。
在 protobuf-net 抱怨缺少序列化器之前,有什么方法可以附加一个钩子来确保我的代码有机会了解 'Foo'?
我还没有完全尝试过这种情况,但是:为了允许一些灵活性,所有 Type
存储和再水化都通过 TypeModel.DynamicTypeFormatting
事件进行;所以,理论上你 可以 将这个事件挂接到 RuntimeTypeModel.Default
,像这样:
RuntimeTypeModel.DynamicTypeFormatting += (sender, args) => {
if (args.FormattedName != null) { // meaning: rehydrating
lock(SomeSyncLock) {
if(NotYetKnown(args.FormattedName))
Prepare(args.FormattedName);
}
}
};
此 API 的 intent 是为了让您控制类型的解析方式,但是...我想它也适用于此吗?
但是,我可以理解在第一次看到新的 Type
时更有针对性的事件的想法,本质上是替换/补充 "apply default behaviour" 代码。不过,我认为 今天 不存在。