在调用期间等待_TransparantProxyStub_CrossContext函数时,WCF最大化CPU

时间:2016-03-03 01:43:37

标签: c# web-services wcf soap wsdl

在使用WCF调用Cisco的AXL SOAP API时,我的CPU使用率很高。我首先使用wsdl生成的类创建服务模型clientbase。我正在使用basichttpbinding和transfermode作为缓冲。执行调用时,CPU最大化,并且CPU配置文件显示在_TransparentProxyStub_CrossContext@0之类的调用之后调用的clr.dll中,96%的CPU时间为base.Channel.getPhone(request);。更准确地说,该调用最大化了正在运行该进程的CPU核心。

以下是来自wsdl generate

的客户端创建片段
[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

您必须从呼叫管理器系统下载Cisco的AXL api的wsdl。我正在使用10.5版本的API。我认为主要的减速与XML处理有关。 api的WSDL非常庞大,结果类生成了538406行代码!

更新2

我已经开启了所有关卡的WCF跟踪。最大的时差是在“写入消息”和“通过频道发送消息”之间的过程动作活动中,其中这两个动作之间几乎整整一分钟。其他活动(构建渠道,开放客户群和关闭客户群)都执行得相对较快。

更新3

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

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

更新4

看起来操作次数是核心问题。我能够通过动词(例如,获取,添加等)将操作和接口定义分离到他们自己的客户端库和接口中(使用sublime文本和正则表达式作为resharper的非常缓慢的过程,并且codemaid无法处理仍然是250K +的大文件线)。对具有约150个操作定义的“Get”客户端的测试导致getPhone与先前的60秒结果相比执行10秒。这仍然比它应该慢得多,因为在fiddler中简单地制作这个操作导致2秒的执行。通过尝试进一步分离操作,解决方案可能会进一步减少操作次数。但是,这增加了将使用此库作为单个客户端的所有系统中断的新问题。

1 个答案:

答案 0 :(得分: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属性。
  2. 这些对于xml处理很有用,但是根据我的理解,生成的类似乎不正确。例如,serviceknowntype对于所有操作都是相同的,即使许多知识类型对于每个操作都是唯一的。这会将生成的文件的总大小从500K +行减少到250K +,客户端实例化时间会略有提升。

    1. 将操作合同从实现接口的客户端库中的接口和方法中分离出来。

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

    3. 这些子客户有三个主要部分。第一部分是原始clientbase客户端的部分类。我希望这个解决方案是向后兼容的,所以我在这里有方法引用子客户端,以便通过调用新的子客户端来调用旧的超级客户端。如果引用了任何实现的操作,静态get访问器将启动子客户端。在调用close或abort时也会添加事件,以便子客户端仍然可以运行这些操作。

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

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

      最后,我已将身份验证移至类似于described here的自定义端点行为。使用IClientMessageInspector发送身份验证标头会立即保存到服务器的一次往返呼叫,其中WCF首先在进行身份验证之前发送匿名请求。根据服务器的不同,这大约增加了2秒。

      总的来说,我的性能从70s增加到2.5s。