使用WPF调用WCF第一次很慢

11

我近日一直在处理我们WPF应用程序的问题,我想知道是否有人遇到过这个问题并能够帮忙解决?

问题似乎归结为客户端动态生成序列化器来处理Web方法调用中的类型。当该方法首次被调用时(即Web服务本身已经运行),可能需要花费8秒钟,随后的调用可能只需要20毫秒。在此延迟期间,客户端WPF进程的CPU占用率非常高。

当使用XmlSerializer时,有一种方法可以使用svcutil预先生成这些序列化程序集。当使用正常的WCF DataContractSerializer时(就像我们一样),似乎没有这个选项。

我的目标是能够预先为所有数据合同中的所有类型生成此程序集(总数很多),或者使用自定义进程替换此进程,并将数据以二进制格式传递(我们拥有这个Web服务/客户端的两端,都是.NET 4)。我已经使用了BinaryFormatter和GZip压缩,虽然这加速了数据传输,但它总是会被还原为XML,以便框架进行反序列化,因此问题仍然存在。

有什么想法吗?

3个回答

8
您可以使用二进制库,例如protobuf-net,它非常快速,即使存在初始启动成本,因为必须为每种类型生成代码,它仍然DataContractSerializerBinaryFormatter好得多。您应该能够节省几秒钟,并获得更流畅的体验。它可以轻松地与WCF集成。请记住,WCF仍将检查您的各种契约以生成正确的WSDL和各种元数据。
还有其他一些可以减慢WCF启动速度的事情,例如确定默认Web代理。如果您没有使用它,请确保在绑定配置中useDefaultWebProxyfalse

尽管进行了优化,你会发现 WCF 启动通常很慢。在类似场景中(我控制了两端,客户端是 WPF 应用程序)厌倦了与缓慢的斗争后,我简单地放弃了 WCF,转而选择了 ServiceStack + protobuf-net。第一个调用从 2-3 秒降至约 100 毫秒,所有后续的 HTTP 调用都非常快速。整体用户体验得到极大改善。请注意,我与 ServiceStack 没有任何关联,这只是我的经验。


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

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

0

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

这里有一些想法...可能会在序列化区域加速事情。

关于为服务类型预生成序列化程序集的问题...在Project|Build中有一个名为“Generate Serialization Assembly”的选项...如果打开,则会在构建时生成程序集,而不是在运行时动态生成。

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

你也可以尝试在客户端和服务器的代码早期进行此操作,以练习DataContract序列化程序...即在客户端或服务器必须处理请求之前...(不确定是否有帮助)。
DataContractSerializer ps = new DataContractSerializer(typeof(Person));
DataContractSerializer cs = new DataContractSerializer(typeof(Company));
etc...

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

1
谢谢 Colin,我已经尝试了以上所有方法,但都没有任何改变。最后一个建议的问题在于,我正在尝试解决的问题是客户端启动缓慢,因此无论我是启动序列化程序还是框架启动序列化程序,它都不会有太大影响,需要相同的时间。大多数冷启动修复都涉及服务器端,这不是问题所在。 "生成序列化程序集"选项仅适用于XmlSerializer,而且仅在VS找到代理(我们有自己的代理)时才有效。 - Simon Evans
这个链接可能会给你一些不同策略的想法... http://www.icodeteam.net/default/post/iCodeTeam/35/Object-Serialization-in-NET-Available-serialization-libraries/ - Colin Smith

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接