winsock资源不足

时间:2014-06-12 08:18:19

标签: c# asp.net web-services time-wait

我们有一个c#web服务和客户端,都是在Visual Studio 2008中创建的(新项目 - > ASP.Net Web服务应用程序)。服务托管在Windows Server 2012 R2,IIS 8.5上。

当客户端将数据发送到我们的服务时,我们将其转发给第三方服务,将结果保存到数据库并将其返回给客户端。

问题在于,在极少数情况下,当我们的服务负载很重(每秒许多请求)时,它会开始抛出“不足的winsock资源可用于完成套接字连接启动”。

我们发现我们的Web服务正在打开许多与第三方服务的TCP连接,并使它们处于TIME_WAIT状态。当此类连接的数量达到较高数量(大约17000)时,整个服务器将无法建立任何新连接。从远程桌面到互联网浏览器的一切都停止工作。这持续几分钟,然后,当Windows开始关闭这些连接时,它会恢复正常。

为了与第三方服务进行通信,我们的服务在其整个生命周期中仅使用一个SoapClient实例。它是在初始化时创建的,永远不会被关闭或销毁;永远不会创建新实例。

BLIND.BLINDSoapClient client = new BLIND.BLINDSoapClient(base.binding, base.address);

将数据发送到第三方服务时,我们只需调用其Web方法,并在不关闭,处理或进行任何清理的情况下保留该方法:

BLIND.Answer answer = client.Search(...);
..save to database
return answer;

我们可以做些什么来避免这种time_wait连接的积累?

有没有更好的方法来管理SoapClient?我们应该为每个请求打开一个新的soap客户端并手动关闭它们吗?

如果相关,我们的绑定设置如下:

      binding = new BasicHttpBinding();
      binding.Name = "SLTDSoap";
      binding.CloseTimeout = TimeSpan.FromSeconds(Timeout);
      binding.OpenTimeout = TimeSpan.FromSeconds(Timeout);
      binding.ReceiveTimeout = TimeSpan.FromSeconds(Timeout);
      binding.SendTimeout = TimeSpan.FromSeconds(Timeout);
      binding.AllowCookies = false;
      binding.BypassProxyOnLocal = false;
      binding.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
      binding.MaxBufferSize = 65536;
      binding.MaxBufferPoolSize = 524288;
      binding.MessageEncoding = WSMessageEncoding.Text;
      binding.TextEncoding = System.Text.Encoding.UTF8;
      binding.TransferMode = TransferMode.Buffered;
      binding.UseDefaultWebProxy = true;

      binding.ReaderQuotas.MaxDepth = 32;
      binding.ReaderQuotas.MaxStringContentLength = 8192;
      binding.ReaderQuotas.MaxArrayLength = 16384;
      binding.ReaderQuotas.MaxBytesPerRead = 4096;
      binding.ReaderQuotas.MaxNameTableCharCount = 16384;

      binding.Security.Mode = (_url.StartsWith("https:")) ? BasicHttpSecurityMode.Transport : BasicHttpSecurityMode.None;
      binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
      binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
      binding.Security.Transport.Realm = "";
      binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
      binding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Default;

System.Net.ServicePointManager.DefaultConnectionLimit = 500;

谢谢!

2 个答案:

答案 0 :(得分:4)

我认为我们可能已经解决了'不足的资源'问题。

我们设置了以下注册表值: HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ services \ Tcpip \ Parameters \ MaxUserPort = 60000 HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ services \ Tcpip \ Parameters \ TcpTimedWaitDelay = 30

我们在偷看时间对生产环境的最大预期负载是每秒150个请求。 这意味着我们将在Windows开始释放之前在30秒内创建4500个连接。 这远低于60000,应该确保不会再发生这个问题。

我们让系统以每秒150个请求的速度运行,这些设置超过3天,问题没有发生。

答案 1 :(得分:0)

根据您的推荐BLIND.BLINDSoapClient继承自System.ServiceModel.ClientBase。这个类是IDisposable,这意味着你在完成它时应该总是处理它(在它调用Close的背景上 - 它关闭背景中的通信对象)。

即:

using(var client = new BLIND.BLINDSoapClient(base.binding, base.address)) {
  // enjoy client
}

最终可能存在一些限制,可能会导致服务器停机。你可以:

  1. 在iis上设置对您的站点/ webservice允许的请求数限制 - 请参阅http://www.iis.net/configreference/system.applicationhost/sites/sitedefaults/limits,在您调用webservice的重负载下,偶尔会失败 - 但在客户端
  2. 将您的Web服务从一个盒子解决方案移动到Web场解决方案(一个盒子充当负载均衡器,根据当前负载将请求委托给多个盒子)
  3. 将您的网络服务移至云端 - 如亚马逊或Azure(同样的2.只有您不必关心负载均衡器)