我们有 ac# Web 服务和客户端,它们都是在 Visual Studio 2008 中创建的(新项目 -> ASP.Net Web 服务应用程序)。服务托管在 Windows server 2012 R2、IIS 8.5 上。
当客户端将数据发送到我们的服务时,我们将其转发给第 3 方服务,将结果保存到数据库并返回给客户端。
问题是,在极少数情况下,当我们的服务负载过重(每秒有很多请求)时,它会开始抛出“Insufficient winsock resources available to complete socket connectioninitiation”。
我们发现我们的 Web 服务正在打开许多到 3rd 方服务的 TCP 连接,并使它们处于 TIME_WAIT 状态。当此类连接的数量达到很高的数量(大约 17000)时,整个服务器将失去建立任何新连接的能力。从远程桌面到互联网浏览器的一切都停止工作。这会持续几分钟,然后,当 Windows 开始关闭这些连接时,它会正常恢复。
对于与第 3 方服务的通信,我们的服务在其整个生命周期中仅使用一个 SoapClient 实例。它是在初始化时创建的,永远不会关闭或销毁;永远不会创建新实例。
BLIND.BLINDSoapClient client = new BLIND.BLINDSoapClient(base.binding, base.address);
将数据发送到 3rd 方服务时,我们只需调用它的 web 方法,并保持原样,无需关闭、处置或进行任何清理:
BLIND.Answer answer = client.Search(...);
..save to database
return answer;
我们可以做些什么来避免这种 time_wait 连接的建立?
有没有更好的方法来管理 SoapClient(s)?我们应该为每个请求打开一个新的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;
谢谢!