Protobuf.net 异常 - 检查元数据时超时
Posted
技术标签:
【中文标题】Protobuf.net 异常 - 检查元数据时超时【英文标题】:Protobuf.net Exception - Timeout while inspecting metadata 【发布时间】:2011-09-10 15:07:04 【问题描述】:我有时在尝试使用 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)
问候, 马克
编辑添加:我这样定义我的可序列化对象:
[ProtoContract]
public class Job
[ProtoMember(1)]
public long JobId get; private set;
我很难在每个可序列化对象上轻松调用 PrepareSerialiser,因为我在不同的命名空间中有很多。但是考虑一下,如果要求 protobuf 在完全相同的时间反序列化两个相同类型的对象,这是它以前从未见过的类型,会发生什么?
【问题讨论】:
嘿,马克。如果我能可靠地重现该问题,我肯定会提供更多信息。不幸的是,这种情况发生在如此小比例 (0.01%) 的情况下,事实证明非常很难确定。我序列化的所有对象都相对较小,可能只有 10 个值类型和几个列表。我会找出可以在哪里添加 prepareSerializer 调用,如果问题仍然存在,请告诉您。 我很感兴趣...我将尝试浏览代码以查找可能导致此问题的任何内容(感谢包含完整的堆栈跟踪,顺便说一句 - 这很有帮助)。这……很奇怪。 值得补充的是,虽然我只有一个线程反序列化一个唯一的字节 [],但我可能有多达 100 个线程同时反序列化 100 个唯一的字节 []。可能是与 CPU 负载有关的问题,即开始反序列化和结束之间的时间太长? 【参考方案1】:老问题,但如果有人碰巧得到这个错误,请检查你正在使用的 DLL 版本。此异常出现在便携版本中的可能性非常高。
便携版有几个与此问题相关的 PR,它们是 https://github.com/mgravell/protobuf-net/pull/98 和 https://github.com/mgravell/protobuf-net/pull/114。
【讨论】:
我在线程环境中使用 2.0.0.640 PCL 库的经历非常一致。在更高级别的项目中添加对显式版本的引用可以通过将 PCL 版本替换为特定于该更高级别程序集(SL、.Net 40 等)的版本来解决此问题,从而立即解决问题。 这个答案和评论为我节省了很多时间。很棒的见解,朋友们。 PCL 版本 2.0.0.668 对我来说始终出现此错误。我还尝试增加上面建议的超时@MarcGravell,但这并不能解决问题。除了切换到 .DLL 的各个 CLR 特定版本之外,还有其他线索吗? 在 2016 年,这仍然是个问题。我在一次部署中不小心用 PCL 版本替换了正常的 2.0.0.668,并且立即出现了许多超时错误。删除 PCL 有助于解决问题。【参考方案2】:RuntimeTypeModel.Default(默认模型)是静态的,是静态 Serializer 类的基础(即没有任何静态状态的注释)。尽管由于偏执而添加了此检查,但我从未看到此错误出现。我非常希望看到一个可以重现这一点的例子。你确定你不是线程?如果不是线程,我只能想知道:类型模型真的真的大吗?
确实,即使有许多线程在启动时积极地攻击它(即在 *** 上),它也表现良好。正如错误消息提示的那样,您可以尝试在应用启动期间调用 Serializer.PrepareSerializer,这将预先初始化所有内容,避免任何线程问题。
但是,嘿!至少没有死锁!
然而,这里的奇怪之处在于它仍然不应该可能死锁 - 它故意使用粗锁来避免锁定顺序的问题。再说一次 - 我真的很想看看样品。
【讨论】:
@MarcF - 作为一个随机的东西,你可以尝试将RuntimeTypeModel.Default.MetadataTimeoutMilliseconds
设置为一个愚蠢的大东西,看看这是否是一个真正的死锁与比正常时间更长的东西?
抱歉这个回复拖了这么久。我刚刚浏览了一些旧的 *** 帖子。我确实将 MetadataTimeoutMilliseconds 增加到 '150000' 并且我认为从那以后我没有看到错误。
只是想补充一下,我也遇到了这个错误。在数千次测试中只有 1 次,但我确实有多个线程在做这项工作。 (4 个大气压,但会随着更多事物的出现而增长)
这只是在我对所有内核执行 CPU 密集型工作时发生的。我的整个机器很慢。许多线程试图获取锁,其中一个线程处于某种“编译”方法中。
@AgileJon 如果你想完全消除它,你可以使用单独的预编译器 exe 生成一个序列化 dll,然后永远不需要做任何元数据检查【参考方案3】:
为我准备序列化器使用
Serializer.PrepareSerializer<Type>();
并没有真正帮助我。
对我来说,解决方案(又名解决方法)是在启动时序列化类型,这会在应用启动时造成问题:
MessageSerialization.Serialize(new Type());
其中MessageSerialization.Serialize是使用protobuf Serializer.Serialize(stream, o)的序列化方法
【讨论】:
【参考方案4】:我的服务器上也出现了同样的错误。虽然我不确定是什么导致了错误。几天前,当我们的服务器处于我们经历过的最高负载时,它每隔几个小时就发生了两次。运行 8 台服务器,所有服务器的 CPU 在几秒钟内从 70% 上升到 100%,但时间略有不同。例如,每个服务器可能在第一个服务器后 1-5 分钟就开始了这个峰值。
以前从未见过这种情况,我已经在生产中使用了几个月的代码。仍然无法重现它,我不知道是否抛出错误是因为服务器处于 100% CPU,或者这是否是导致服务器峰值的原因。停止与服务器的所有连接并让 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);
【讨论】:
以上是关于Protobuf.net 异常 - 检查元数据时超时的主要内容,如果未能解决你的问题,请参考以下文章
Protobuf.net “一旦生成了序列化程序,就无法更改类型”
Protobuf-net 序列化错误 =“一旦生成了序列化程序,就无法更改类型”