问题 首次使用时,从WPF使用WCF非常慢


我的WPF应用程序出现问题已经困难了几天,我想知道是否有人之前遇到过这个问题并且可以提供帮助? 问题似乎归结为客户端生成“即时”序列化程序来处理该Web方法调用中的类型。当第一次调用该方法时(Web服务本身已经运行),可能需要例如8秒,后续通话可能需要20毫秒。在此延迟期间,客户端WPF进程上的CPU为v。

使用XmlSerializer时,有一种方法可以使用svcutil预生成这些序列化程序集。当(正如我们)使用普通的WCF DataContractSerializer时,此选项似乎不存在。

我想要的是能够为我的所有数据合同(很多)中的所有类型预生成此程序集,或者替换为使用我可以编码的自定义程序替换此过程并以二进制形式传递数据(我们拥有这个webservice / client的两端,它们都是.NET 4)。我已经使用了BinaryForamtter和GZip压缩,虽然这加速了数据的传输,但它总是被恢复为XML以被框架反序列化,因此这个问题仍然存在。

有任何想法吗?


4850
2017-10-11 09:03


起源



答案:


您可以使用二进制库,如 protobuf网,这是非常快的,即使有初始启动成本,因为必须为每种类型生成代码,它仍然是 比...更好的方式 DataContractSerializer 要么 BinaryFormatter。您应该获得几秒钟,并获得整体更顺畅的体验。有可能 与WCF轻松集成。请记住,WCF仍将检查您的各种合同,以生成正确的WSDL和各种元数据。

还有其他因素可能会降低WCF启动速度,例如确定默认Web代理。确保这一点 useDefaultWebProxy 是 false 在您的绑定配置中,如果您没有任何用途。

不过,无论你采取什么措施来优化它,你都会发现WCF启动通常很慢。就个人而言,厌倦了类似场景中的缓慢战斗(我控制了两端,而客户端是一个WPF应用程序),我只是放弃了WCF并且去了 ServiceStack + protobuf-net。第一个呼叫从2-3秒到大约100毫秒,所有后续的HTTP呼叫都是瞬时的。整体用户体验大大提升。请注意,我与ServiceStack没有任何关系,这只是我的经验。


8
2017-10-11 09:27



谢谢Julien,我已经使用了useDefaultWebProxy false。我已经看过使用protobuf-net,但我无法理解它需要我用新属性重新装饰我的所有数据类型 - 我希望不是。它在网站上说,如果你愿意,v2可以“没有属性使用”,但所有的例子都使用属性。这会改善第一次调用的启动速度,即使在IIS(实际上是强制要求的)中,也就是在构建时预先生成的protobuf-net序列化器? - Simon Evans
@SimonEvans,只要你有 Order 每个人的财产 DataMember你应该没问题。虽然我没有自己使用它(我从一开始就使用protobuf)。 - Julien Lebosquain
朱利安,你的意思是我必须为每个公共财产添加[ProtoMember(n)]吗?谢谢你的帮助 - Simon Evans
@SimonEvans:如果你想要真正快速启动,你可以将protobuf-net序列化程序预编译到一个可以引用的程序集。至于 ProtoMember(n),它没有必要: DataMember(Order = n) 具有相同的效果。至于“它真的会提高启动速度”,我无法向你保证,在示例项目中尝试一下。 - Julien Lebosquain
谢谢Julien,目前我的类型上没有任何属性设置(除了KnownType和XmlInclude)。默认情况下,Datacontractserializer将假定所有类型和所有成员都要序列化(这是我们想要的)。我将不得不尝试装饰几种类型,看看我是否可以使用protobuf-net,麻烦的是我们有数百种(其中一些是生成的)。 - Simon Evans


答案:


您可以使用二进制库,如 protobuf网,这是非常快的,即使有初始启动成本,因为必须为每种类型生成代码,它仍然是 比...更好的方式 DataContractSerializer 要么 BinaryFormatter。您应该获得几秒钟,并获得整体更顺畅的体验。有可能 与WCF轻松集成。请记住,WCF仍将检查您的各种合同,以生成正确的WSDL和各种元数据。

还有其他因素可能会降低WCF启动速度,例如确定默认Web代理。确保这一点 useDefaultWebProxy 是 false 在您的绑定配置中,如果您没有任何用途。

不过,无论你采取什么措施来优化它,你都会发现WCF启动通常很慢。就个人而言,厌倦了类似场景中的缓慢战斗(我控制了两端,而客户端是一个WPF应用程序),我只是放弃了WCF并且去了 ServiceStack + protobuf-net。第一个呼叫从2-3秒到大约100毫秒,所有后续的HTTP呼叫都是瞬时的。整体用户体验大大提升。请注意,我与ServiceStack没有任何关系,这只是我的经验。


8
2017-10-11 09:27



谢谢Julien,我已经使用了useDefaultWebProxy false。我已经看过使用protobuf-net,但我无法理解它需要我用新属性重新装饰我的所有数据类型 - 我希望不是。它在网站上说,如果你愿意,v2可以“没有属性使用”,但所有的例子都使用属性。这会改善第一次调用的启动速度,即使在IIS(实际上是强制要求的)中,也就是在构建时预先生成的protobuf-net序列化器? - Simon Evans
@SimonEvans,只要你有 Order 每个人的财产 DataMember你应该没问题。虽然我没有自己使用它(我从一开始就使用protobuf)。 - Julien Lebosquain
朱利安,你的意思是我必须为每个公共财产添加[ProtoMember(n)]吗?谢谢你的帮助 - Simon Evans
@SimonEvans:如果你想要真正快速启动,你可以将protobuf-net序列化程序预编译到一个可以引用的程序集。至于 ProtoMember(n),它没有必要: DataMember(Order = n) 具有相同的效果。至于“它真的会提高启动速度”,我无法向你保证,在示例项目中尝试一下。 - Julien Lebosquain
谢谢Julien,目前我的类型上没有任何属性设置(除了KnownType和XmlInclude)。默认情况下,Datacontractserializer将假定所有类型和所有成员都要序列化(这是我们想要的)。我将不得不尝试装饰几种类型,看看我是否可以使用protobuf-net,麻烦的是我们有数百种(其中一些是生成的)。 - Simon Evans


您是否通过查看生成的服务引用确认DataContractSerializer确实正在使用?由于添加服务引用操作期间的某些模式不匹配,可能会生成XmlSerializer代码而不是默认的DataContractSerializer,这会导致此行为是XmlSerializer的典型特征。在这种情况下,如您所述 - 您可以预先生成序列化代码以改善冷启动: http://msdn.microsoft.com/en-us/library/aa751883.aspx。 谢谢。


2
2017-10-12 18:06





您可以通过预加载WCF服务来改善冷启动时间......即。不要等待第一个请求导致它被加载...提前加载。

只是一些想法......“可能”有助于加速序列化领域的事情。

关于为您的服务类型预生成序列化程序集.... Project | Build中有一个名为“Generate Serialization Assembly”的选项....如果切换为“On”,那么它会在Build time生成程序集而不是在运行时动态。

目前尚不清楚此选项是仅用于基于XMLSerializer的序列化程序的预生成序列化程序集,还是用于DataContractSerializers。您可以尝试将其切换为“开”以查看它是否有所作为。

您也可以尝试在客户端和服务器的代码中尽早执行此操作,以运行DataContract序列化程序...即。在客户端或服务器必须处理请求之前...(不确定它是否有帮助)。

DataContractSerializer ps = new DataContractSerializer(typeof(Person));
DataContractSerializer cs = new DataContractSerializer(typeof(Company));
etc...

为了使其更易于维护,您可以编写一个使用反射来定位您要序列化的类型的例程,例如寻找标有DataContract ....或其他一些启发式......或预定义表格的类型。


0
2017-10-11 10:00



感谢科林,我已经尝试了以上所有内容,但这些都没有任何区别。最后一个建议的问题是我试图处理的问题是客户端的启动缓慢,所以如果我为序列化器启动它或者如果框架这样做那将无关紧要,它将需要相同的时间。大多数冷启动修复与服务器端有关,这不是问题。 “生成序列化程序集”选项仅适用于XmlSerializer,然后仅在VS找到代理(我们拥有自己的代理)时才有效。 - Simon Evans
这个链接可能会给你一些不同策略的想法.... icodeteam.net/default/post/iCodeTeam/35/... - Colin Smith