异常-检查元数据时超时

本文关键字:超时 元数据 检查 异常 | 更新日期: 2023-09-27 18:08:26

有时在尝试使用protobuf.net反序列化对象时收到以下异常。我很惊讶,因为我从来没有超过一个线程同时对同一个对象进行反序列化,而且protobuf.net源代码似乎没有使用任何静态对象进行反序列化。例外确实建议一个解决方案,但我不确定如何实现,所以欢迎一个例子。

Base Exception Type:
System.TimeoutException: Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection
at ProtoBuf.Meta.RuntimeTypeModel.TakeLock(Boolean& lockTaken)
at ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(Type type, Boolean demand, Boolean addWithContractOnly, Boolean addEvenIfAutoDisabled)
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)
Inner Exception Type:
System.TimeoutException: Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection
at ProtoBuf.Meta.RuntimeTypeModel.TakeLock(Boolean& lockTaken)
at ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(Type type, Boolean demand, Boolean addWithContractOnly, Boolean addEvenIfAutoDisabled)
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)
Stack Trace: 
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)
at ProtoBuf.Meta.TypeModel.GetKey(Type& type)
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type)

问候,Marc

我这样定义我的可序列化对象:
[ProtoContract]
public class Job
{
    [ProtoMember(1)]
    public long JobId { get; private set; } 
}

我很难在每个可序列化对象上轻松调用PrepareSerialiser,因为我在不同的命名空间中有许多对象。然而,想想看,如果protobuf被要求同时反序列化两个相同类型的对象,一个它以前从未见过的对象,会发生什么?

异常-检查元数据时超时

老问题,但如果有人碰巧得到这个错误,请检查您正在使用的DLL版本。此异常在可移植版本中出现的几率非常高。

有几个pr与这个问题有关的便携版本是https://github.com/mgravell/protobuf-net/pull/98和https://github.com/mgravell/protobuf-net/pull/114。

RuntimeTypeModel。Default(默认模型)是静态的,并支持静态Serializer类(请注意没有任何静态状态的注释)。尽管由于偏执而添加了此检查,但我从未看到此错误被引发。我非常希望能看到一个能再现这一点的例子。你确定你不是在穿线吗?如果不是为了线程,我只能想:类型模型真的真的大吗?

事实上,即使在启动时有许多线程猛烈地攻击它(例如在这里的stackoverflow上),它也表现得很好。正如错误消息提示的那样,您可以尝试调用Serializer。在应用程序启动时PrepareSerializer,它将预先初始化一切,避免任何线程问题。

但嘿!至少它没有陷入僵局!

但是,这里的奇怪之处在于仍然不应该可能发生死锁—它故意使用粗锁来避免它取锁的顺序带来的问题。我真的很想看到一个例子。

为我准备序列化器使用

Serializer.PrepareSerializer<Type>();

并没有真正帮助我。

对我来说,解决方案(又名变通方案)是在启动时序列化类型,这在应用程序启动时造成了麻烦:

MessageSerialization.Serialize(new Type());

MessageSerialization的地方。Serialize是使用protobuf Serializer的序列化方法。序列化(流,o)

我在我的服务器上得到同样的确切错误。虽然我不确定是什么导致错误。几天前,当我们的服务器处于我们所经历过的最高负载下时,它发生了两次,相隔几个小时。在运行8台服务器时,所有服务器上的CPU在几秒钟内从70%上升到100%,但时间略有不同。例如,每个服务器可能在第一个峰值之后1-5分钟开始这个峰值。

以前从未见过这种情况,我已经在生产中使用了几个月的代码。我还没能重现它,我不知道这个错误是由于服务器的CPU达到100%而抛出的,还是这就是导致服务器尖峰的原因。停止与服务器的所有连接并让cpu降回0可以修复这个问题。不需要重新启动iis。

当IIS启动时,我为每种类型运行一次以下代码:

var type = this.GetType();
RuntimeTypeModel.Default.Add(type, true);
Int32 i = 1;
foreach(PropertyInfo info in type.GetProperties())
{
    if(info.CanWrite)
    {
        RuntimeTypeModel.Default[type].AddField(i++, info.Name);
    }
}