c# - WCF 在调用期间等待 _TransparantProxyStub_CrossContext 函数时使 CPU 最大化

标签 c# web-services wcf soap wsdl

在使用 WCF 调用 Cisco 的 AXL SOAP API 时,我的 CPU 使用率很高。我首先使用从 wsdl 生成的类创建服务模型客户端库。我正在使用 basichttpbinding 和 transfermode 作为缓冲。执行调用时,CPU 达到极限,CPU 配置文件显示 96% 的 CPU 时间在 _TransparentProxyStub_CrossContext@0 来自 clr.dll,在 base.Channel 等调用之后调用.getPhone(请求);。更准确地说,该调用使运行该进程的 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

您必须从调用管理器系统下载 Cisco 的 AXL api 的 wsdl。我使用的是 10.5 版本的 API。我相信主要的减速与 XML 处理有关。 api 的 WSDL 非常庞大,生成的类有 538406 行代码!

更新 2

我已经打开所有级别的 WCF 跟踪。最大的时间差异出现在“已写入消息”和“通过 channel 发送消息”之间的流程操作事件中,这两个操作之间几乎过了整整一分钟。其他事件(构建 channel 、打开客户群和关闭客户群)都执行得相对较快。

更新 3

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

我还注意到我有大约 900 个针对单个服务接口(interface)和端点的操作契约(Contract)。这是由于 AXL API 的 wsdl 将所有操作分组在一个命名空间下。我正在考虑将其分解,但这意味着创建多个客户端库,每个客户端库将实现一个简化的接口(interface),并最终破坏实现此 wcf 库的所有内容。

更新 4

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

最佳答案

我终于解决了这个问题。根本原因似乎确实是操作次数。将生成的客户端从 900 多个操作拆分为 12 个操作(遵循 this question 的指导)后,我能够将生成请求所花费的处理器时间减少到几乎为零。

这是优化从 Cisco 的 AXL wsdl 生成的服务客户端的最终过程:

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

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

处理生成的客户端文件以分解为子客户端:

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

  1. 删除 ServiceKnownTypeFaultContractXmlInclude 属性。

这些对于 xml 处理很有用,但据我所知,生成的类似乎不正确。例如,服务已知类型对于所有操作都是相同的,即使许多已知类型对于每个操作都是唯一的。这将生成的文件的总大小从 500K+ 行减少到 250K+,客户端实例化时间的性能略有增加。

  1. 将接口(interface)和方法的操作契约与实现该接口(interface)的客户端分开。

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

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

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

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

最后,我将身份验证移至类似于 described here 的自定义端点行为.使用 IClientMessageInspector 立即发送身份验证 header 可以节省对服务器的一次往返调用,WCF 喜欢在服务器进行身份验证之前先发送匿名请求。这使我大约增加了 2 秒,具体取决于服务器。

总体而言,我的性能从 70 秒提高到 2.5 秒。

关于c# - WCF 在调用期间等待 _TransparantProxyStub_CrossContext 函数时使 CPU 最大化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35761583/

相关文章:

asp.net - 仅在成功验证后才将 ajax 表单发送到 Web 服务

wcf - EF4 POCO WCF 序列化问题(无延迟加载、代理/无代理、循环引用等)

.net - 无法让 JSONP 与 WCF 数据服务一起使用

c# - 如何异步调用方法

java - 如何禁用 SOAP 1.1 与 Apache CXF 的绑定(bind)

java - 用于具有基本身份验证的 SOAP wsdl 的 AXIS2 Java 客户端

asp.net-mvc - Azure 可扩展环境中的 WebSocket

c# - Array.ForEach 无法从用法推断数组元素类型

c# - Windows Phone - 如何首次设置 LocalSettings?

c# - 检查实例是否为空的扩展方法