2016-03-03 81 views
7

当使用WCF调用Cisco的AXL SOAP API时,CPU占用率过高。我从使用wsdl生成的类创建服务模型客户端开始。我使用basichttpbinding和transfermode作为缓冲。执行呼叫时,CPU会最大化,而CPU配置文件显示,在调用(如base.Channel.getPhone(request);)之后调用的clr.dll中,96%的CPU时间为[email protected]。更正确地说,这个调用最大化了进程正在运行的CPU核心。在调用期间等待_TransparantProxyStub_CrossContext函数时,WCF maxes CPU

下面是从WSDL客户创造剪断产生

[System.Diagnostics.DebuggerStepThroughAttribute()] 
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] 
public partial class AXLPortClient : System.ServiceModel.ClientBase<AxlNetClient.AXLPort>, AxlNetClient.AXLPort 
{ 

    public AXLPortClient() 
    { 
    } 

    public AXLPortClient(string endpointConfigurationName) : 
      base(endpointConfigurationName) 
    { 
    } 

    ... 

这是我如何创建客户端:

public class AxlClientFactory : IAxlClientFactory 
{ 
    private const string AxlEndpointUrlFormat = "https://{0}:8443/axl/"; 

    public AXLPortClient CreateClient(IUcClientSettings settings) 
    { 
     ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true; 
     ServicePointManager.Expect100Continue = false;      

     var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport); 
     basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic; 

     basicHttpBinding.MaxReceivedMessageSize = 20000000; 
     basicHttpBinding.MaxBufferSize = 20000000; 
     basicHttpBinding.MaxBufferPoolSize = 20000000; 

     basicHttpBinding.ReaderQuotas.MaxDepth = 32; 
     basicHttpBinding.ReaderQuotas.MaxArrayLength = 20000000; 
     basicHttpBinding.ReaderQuotas.MaxStringContentLength = 20000000; 

     basicHttpBinding.TransferMode = TransferMode.Buffered; 
     //basicHttpBinding.UseDefaultWebProxy = false; 

     var axlEndpointUrl = string.Format(AxlEndpointUrlFormat, settings.Server); 
     var endpointAddress = new EndpointAddress(axlEndpointUrl); 
     var axlClient = new AXLPortClient(basicHttpBinding, endpointAddress); 
     axlClient.ClientCredentials.UserName.UserName = settings.User; 
     axlClient.ClientCredentials.UserName.Password = settings.Password; 
     return axlClient; 
    } 
} 

为AXL API生成的WSDL代码是非常大的。尽管后续调用速度更快,但初始和后续调用都有CPU问题。还有什么我可以做的调试这个问题?有没有办法来减少这种高CPU使用率?与赏金

更新

多一点信息:

我创建了C#类,像这样:

svcutil AXLAPI.wsdl AXLEnums.xsd AXLSoap.xsd /t:code /l:C# /o:Client.cs /n:*,AxlNetClient 

您必须下载思科的AXL API的WSDL从呼叫管理系统。我正在使用API​​的10.5版本。我认为主要的放缓与XML处理有关。 api的WSDL非常庞大,所产生的类会产生538406行代码!

更新2

我打开WCF与各级跟踪。最大的时间差异在“消息写入”和“通过通道发送消息”之间的过程操作活动中,在这两个操作之间几乎需要一整分钟。其他活动(构建通道,开放客户端和关闭客户端)都执行得相对较快。

更新3

我已经做了两个修改生成的客户端类。首先,我从所有运营合同中删除了ServiceKnownTypeAttribute。其次,我从一些可序列化的类中删除了XmlIncludeAtribute。这两项更改将生成的客户端的文件大小减少了50%以上,并且对测试时间影响很小(70s测试结果减少了大约10s)。

我还注意到,对于单个服务接口和端点,我大约有900个操作合同。这是由于AXL API的wsdl将单个命名空间下的所有操作分组在一起。我正在考虑打破这种情况,但这意味着要创建多个客户端,每个客户端都会实现一个简化的界面,并最终打破实现这个wcf库的所有内容。

更新4

它看起来像操作的数量是核心问题。我能够用动词(例如,动词)分离出操作和界面定义。获取,添加等)到他们自己的客户端和接口中(使用崇高文本和正则表达式作为resharper和codemaid无法处理大型文件仍然是250K +行)的非常慢的过程。与定义了大约150个操作的“Get”客户端的测试相比,前60秒的结果导致getPhone执行10秒。这仍然比应该做得慢很多,就像在小提琴手的结果中简单地在2秒内执行这个操作一样。通过尝试进一步分离操作,该解决方案可能会进一步减少操作次数。但是,这增加了一个新的问题,即打破将这个库用作单个客户端的所有系统。

+0

由于这不是一个确凿的回应,我会分享一个类似的经验。我创建了一个包含许多操作和大型wsdl的wcf。我有一个类似的问题,高CPU使用率。我解决了这个问题,在wcf项目上设置序列化程序集。在项目属性build部分中,您可以尝试将“生成序列化程序集”设置为“开”,并根据您的方式将“Xml文档”设置为文件夹“bin \ release”或“bin \ debug”构建您的解决方案让我知道如果工作,我可以写一个答案并发布更多有关此解决方法。 –

+0

我没有从设置这两个选项增加性能。我在发布配置中运行单元测试,并将序列化设置为auto,根据此(https://stackoverflow.com/questions/9187248/when-to-change-the-generate-serialization-assembly- value)表示序列化已打开已经。 Xml文档是用于将注释翻译成intellisence afik,但我仍然尝试过。 –

回答

1

我终于明白了这个问题。根本原因似乎是操作的数量。将生成的客户端从900多个操作分解到每个12个(根据this question的指导)之后,我可以将处理器花费在生成请求上的时间减少到几乎为零。

这是从思科的AXL的wsdl优化所产生的服务的客户端的最后工序:

使用WSDL像这样生成的客户端代码:

svcutil AXLAPI.wsdl AXLEnums.xsd AXLSoap.xsd /t:code /l:C# /o:Client.cs /n:*,AxlNetClient 

过程所生成的客户机文件分手到子客户端:

我创建了this script来处理生成的代码。该脚本执行以下操作:

  1. 删除ServiceKnownTypeFaultContractXmlInclude属性。

这些对于xml处理很有用,但是生成的类看起来不正确。例如,serviceknowntype对于所有操作都是相同的,即使许多知识类型对于每个操作都是唯一的。这会将生成的文件的总大小从500K +行减少到250K +,而客户端实例化时间会稍微提高性能。

  1. 从实现该接口的客户端接口和方法中分离出操作契约。

  2. 创建子客户端,每个客户端有12个操作及其各自的实现。

这些子客户端有三个主要部分。第一部分是原始的客户端客户端的部分类。我希望这个解决方案向后兼容,所以我在这里有引用子客户端的方法,以便通过调用新的子客户端来调用旧的超级客户端。如果引用了任何已实现的操作,则静态获取访问器将启动子客户端。还有一些事件被添加用于关闭或中止时被调用,以便子客户端仍可以运行这些操作。

子客户端的第二部分和第三部分是实现12个操作的接口和子客户端类。

然后,我从原始生成的客户端中删除了接口和客户端方法。我替换原始客户端的客户端构造函数以简单地存储子客户端在需要时使用的绑定和端点数据。关闭和中止调用被重新创建为每个子客户端在实例化时将订阅的事件调用者。

最后,我已将身份验证移至类似于described here的自定义终结点行为。使用IClientMessageInspector发送身份验证标头将立即保存到WCF希望在验证之前首先发送匿名请求的服务器的一个往返呼叫。这给了我大约2秒的增加,这取决于服务器。总的来说,我的表现从70年代增加到了2.5年代。