我有一个应该能够向我的wcf服务发送数百个并发请求的应用程序。该服务正在公开http联合绑定。
目前我正在使用单个客户端和TPL在多个线程上发送请求。我还将system.net max connections设置更改为1000,以便windows不会将wcf限制为最多2个并发请求。
我可以看到初始请求需要更多时间,因为他们正在获取身份验证令牌等,然后请求时间通常开始显着减少,但后来我间歇性地看到一些峰值,这与服务器日志无关有
我想知道这是否是接近高通量高可扩展客户端的最佳方式。
我尝试在多个线程之间使用共享客户端代理,并在多个线程之间共享ChannelFactory。所有线程都使用TPL完成。
显示绑定
<basicHttpBinding>
<binding name="Binding1" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:05:00" closeTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
答案 0 :(得分:2)
您提到您正在使用单个客户端实例,然后使用TPL扇出工作。 WCF客户端实例在技术上不是线程安全的。创建客户端的成本非常低廉,只要在完成后Close()
,就不应该害怕创建新实例。
接下来,您不应该单独使用TPL来获得并发性。当您进行WCF调用时,它都是关于网络I / O并等待来自服务器的响应。您需要确保在客户端使用async WCF contracts。然后,您可以将这些与TPL的Task.Factory.FromAsync
结合起来,将您在呼叫返回后可能需要执行的任何其他工作流链接在一起。例如:
[ServiceContract]
public interface IMyService
{
[OperationContract(AsyncPattern=true)]
IAsyncResult BeginDoSomething();
SomeResultType EndDoSomething(IAsyncResult result);
}
// Somewhere in the client app you store your channel factory (these are "expensive", these you cache)
ChannelFactory<IMyService> channelFactory = new ChannelFactory<IMyService>();
public void MyClientMethod()
{
// Create a client channel
IMyService myServiceChannel = channelFactory.CreateChannel();
// Use TPL's FromAsync to invoke the async WCF call and wrap that up with the familiar Task API
Task<SomeResultType>.Factory.FromAsync(myServiceChannel.BeginDoSomething
myServiceChannel.EndDoSomething,
null)
.ContinueWith(antecdent =>
{
try
{
// NOTE: exception will be thrown here if operation failed
SomeResultType result = antecedent.Result;
// ... continue processing the result ...
}
finally
{
// NOTE: depending on your configuration you might want to watch for errors and .Abort() here too
((IClientChannel)myServiceChannel).Close();
}
});
}
您的所有可扩展性都来自于在该客户端调用上使用异步I / O.
答案 1 :(得分:1)
WCF服务和使用WCF客户端(直接使用ClientBase或ChannelFactory)的一些生产性能问题是由于.NET垃圾收集器由于草率的对象处理而被迫执行大量工作。我将使用性能监视器在运行时检查.NET垃圾收集器的行为,看看是否会导致峰值。
如果是GC问题,那么您需要检查您的服务和客户端代码。在服务上,确保对象实例范围尽可能受限。这有助于确保实例主要是在Gen0中进行垃圾收集,这种情况最常发生。还应审查客户端代码,但这在服务代码中尤为重要。
此外,在服务中,特别是在客户端上,确保实现IDisposable的对象实例正确地包装在using
语句除了WCF客户端实例之外。要处理WCF客户端的正确处置,请查看此brief blog post以获得良好的模式。如果您使用依赖注入容器,这可能会变得棘手。您可以搜索“WCF不使用”以获取有关原因的更多详细信息。 BasicHttpBinding和WsHttpBinding的一些配置可以处理草率处理,但任何使用会话的绑定都会容易出现GC问题。