WCF生成客户端与HttpClient调用对比及性能优化咨询
问题:WCF客户端调用性能远低于HttpClient/SOAP UI的疑问
近期集成外部API时首次使用WCF,通过svcutil工具从供应商提供的WSDL生成C# WCF客户端,配置绑定后调用正常,但client.GetAllItems()耗时约12000ms;而用SOAP UI或自行编写HttpClient调用仅需3000ms。
WCF客户端调用代码
using (ServiceClient client = new ServiceClient()) { System.Xml.XmlElement resp = client.GetAllItems(new ItemRequest() { Key = "License Key"}); var items = DataSerializer.DeserializeXml<ExternalItemCollection>(resp.InnerXml); foreach (var item in items) { _logger.LogInfo("Item Id is" + item.Id); } }
自行编写的HttpClient调用代码
using (var client = new System.Net.Http.HttpClient()) { client.DefaultRequestHeaders.TryAddWithoutValidation("apikey", request.Key); client.DefaultRequestHeaders.TryAddWithoutValidation("SOAPAction", "http://url.for.wcf/GetAllItems"); client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "text/xml"); var getAllItemsReq = $@"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/""> <soapenv:Header> <web:AuthInfo> <web:LicenseKey>{request.Key}</web:SiteID> </web:AuthInfo> </soapenv:Header> <soapenv:Body> <web:GetAllItems> <web:status>Current</web:status> </web:GetAllItems> </soapenv:Body> </soapenv:Envelope>"; var resp = client.PostAsync("https://api.external.vendor/service.svc", new System.Net.Http.StringContent(getAllItemsReq, System.Text.Encoding.UTF8, "text/xml")); resp.Wait(); resp.Result.EnsureSuccessStatusCode(); var xmlResp = resp.Result.Content.ReadAsStringAsync(); xmlResp.Wait(); var xmlReader = System.Xml.XmlReader.Create(new System.IO.StringReader(xmlResp.Result)); xmlReader.ReadToDescendant("GetAllItemsResponse"); return xmlReader.ReadInnerXml(); }
WCF客户端配置
<system.serviceModel> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_IExternalService" maxReceivedMessageSize="2147483647"> <security mode="Transport" /> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="https://website.com/myService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IExternalService" contract="IExternalService" name="BasicHttpBinding_IExternalService" /> </client> </system.serviceModel>
咨询问题:
- 使用HttpClient替代WCF生成客户端是否存在隐患?
- 如何优化WCF调用性能,使其耗时接近HttpClient调用?
一、使用HttpClient替代WCF的隐患
- 手动维护SOAP报文成本高:如果供应商更新WSDL(比如新增字段、修改接口参数),你得手动修改XML模板,很容易出错;而WCF只需重新生成客户端就能自动适配。
- 缺失WCF内置特性:事务处理、消息可靠传递、自动重试、强类型异常封装(WCF会把服务端SOAP Fault转成强类型异常)这些特性都得自己手动实现,工作量大且容易遗漏。
- 序列化/反序列化重复造轮子:WCF自动处理XML和对象的序列化,用HttpClient得自己写XML解析逻辑,代码冗余,遇到复杂嵌套类型、数组时更容易出问题。
- 调试监控不便:WCF集成了.NET的诊断工具(比如ETW追踪),用HttpClient得自己实现日志、追踪逻辑,排查问题难度更高。
二、优化WCF调用性能的方法
1. 调整绑定配置参数
修改basicHttpBinding的配置,添加以下优化项,减少连接和处理开销:
<basicHttpBinding> <binding name="BasicHttpBinding_IExternalService" maxReceivedMessageSize="2147483647" sendTimeout="00:01:00" receiveTimeout="00:01:00" openTimeout="00:00:10" closeTimeout="00:00:10" useDefaultWebProxy="false" <!-- 不需要代理时关闭,避免额外延迟 --> allowCookies="false" bypassProxyOnLocal="false"> <security mode="Transport" /> <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" /> <httpTransport keepAliveEnabled="true" <!-- 启用长连接,复用TCP连接 --> maxBufferSize="2147483647" maxBufferPoolSize="2147483647" /> </binding> </basicHttpBinding>
useDefaultWebProxy="false":无需代理时关闭,避免代理检测或连接的延迟。keepAliveEnabled="true":复用TCP连接,减少每次调用的握手时间。- 缩短超时参数:把
openTimeout缩到10秒,避免不必要的等待。
2. 复用WCF客户端实例
WCF客户端的创建和通道初始化很耗时,当前代码每次调用都新建实例并销毁,会重复创建连接。可以复用实例:
// 全局维护单例客户端(注意线程安全,多线程场景可考虑通道池) private static ServiceClient _client; public static void InitClient() { if (_client == null || _client.State != CommunicationState.Opened) { _client = new ServiceClient(); _client.Open(); // 提前打开通道,避免首次调用的初始化开销 } } // 调用代码 InitClient(); XmlElement resp = _client.GetAllItems(new ItemRequest() { Key = "License Key" }); // ...后续处理逻辑
注意:WCF客户端不是线程安全的,多线程场景下可以用通道池,或者每个线程维护独立实例。
3. 关闭自动元数据获取与地址解析
WCF默认会尝试获取服务元数据、解析地址DNS,可能导致延迟。可以在配置中关闭:
<client> <endpoint address="https://website.com/myService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IExternalService" contract="IExternalService" name="BasicHttpBinding_IExternalService" /> <metadata> <policyImporters> <extension type="System.ServiceModel.Channels.HttpPolicyImporter, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </policyImporters> <wsdlImporters> <extension type="System.ServiceModel.Channels.HttpWsdlImporter, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> </wsdlImporters> </metadata> </client>
或者代码中禁用:
var client = new ServiceClient(); client.Endpoint.Behaviors.Remove(typeof(MetadataExchangeClient));
4. 优化序列化配置
WCF默认的XmlSerializer效率不如DataContractSerializer,生成客户端时可以指定更高效的序列化方式:
- 用
svcutil生成客户端时添加参数:svcutil /serializer:DataContractSerializer your.wsdl - 确保绑定中的
readerQuotas参数足够大,避免因XML大小限制导致的额外处理。
5. 排查DNS解析延迟
首次调用慢可能是DNS解析问题,可以提前解析IP地址,或者在hosts文件中添加域名-IP映射,减少DNS查询时间。
6. 启用WCF性能追踪
开启追踪日志,定位具体耗时环节(比如通道打开、序列化、网络传输):
<system.diagnostics> <sources> <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true"> <listeners> <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="wcf_trace.svclog" /> </listeners> </source> </sources> </system.diagnostics>
生成的wcf_trace.svclog可以用WCF Service Trace Viewer工具打开,分析每个步骤的耗时。
内容的提问来源于stack exchange,提问作者97WaterPolo
相关产品推荐
相关产品推荐

