【发布时间】:2010-10-28 22:03:48
【问题描述】:
我一直在开发一个使用 WCF 访问服务器端逻辑和数据库的 WPF 应用程序。
我从一个 WCF 客户端代理对象开始,我反复使用它来调用服务器上的方法。使用代理一段时间后,服务器最终会抛出异常:
System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at http://.../Service/BillingService.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.
我认为这是因为每个服务调用都打开了一个从代理到服务器的新套接字,而从未关闭它们。最终服务器被淹没并开始拒绝请求。
经过短暂的搜索后,我确定我需要定期关闭()代理。我发现的样本非常小。 This one 提供了一些有用的提示,但并没有真正回答问题。我还 seen recommendations 避免使用 using() 模式(并应用 try/catch/finally),因为代理的 Dispose 方法可能会引发异常(糟糕)。
似乎推荐的模式是这样形成的:
[TestClass]
public class WCFClientUnitTest
{
BillingServiceClient _service;
[TestMethod]
public void TestGetAddressModel()
{
List<CustomerModel> customers = null;
try
{
_service = new BillingServiceClient();
customers = _service.GetCustomers().ToList();
}
catch
{
_service.Abort();
_service = null;
throw;
}
finally
{
if ((_service != null) &&
(_service.State == System.ServiceModel.CommunicationState.Opened))
_service.Close();
_service = null;
}
if (customers != null)
foreach (CustomerModel customer in customers)
{
try
{
_service = new BillingServiceClient();
AddressModel address = (AddressModel)_service.GetAddressModel(customer.CustomerID);
Assert.IsNotNull(address, "GetAddressModel returned null");
}
catch
{
_service.Abort();
_service = null;
throw;
}
finally
{
if ((_service != null) &&
(_service.State == System.ServiceModel.CommunicationState.Opened))
_service.Close();
_service = null;
}
}
}
所以我的问题仍然围绕着我应该让客户端代理保持多长时间?我应该为每个服务请求打开/关闭它吗?这对我来说似乎太过分了。我不会对性能造成重大影响吗?
我真正想做的是创建并打开一个通道,并在通道上进行短暂的重复、短、连续的服务调用。然后很好地关闭频道。
附带说明,虽然我还没有实现它,但我很快就会向服务(SSL 和 ACL)添加一个安全模型,以限制谁可以调用服务方法。 this post 的答案之一提到重新协商身份验证和安全上下文会使为每个服务调用重新打开通道很浪费,但只是建议避免构建安全上下文。
编辑 2010 年 11 月 3 日:这似乎很重要,所以我将其添加到问题中......
作为对Andrew Shepherd's 评论/建议的回应,我在关闭 TrendMicro AntiVirus 的情况下重新运行了单元测试,同时监视 netstat -b 的输出。 Netstat 能够记录 WebDev.WebServer40.exe 拥有的开放端口的显着增长。绝大多数端口处于 TIME_WAIT 状态。 Microsoft says 客户端关闭连接后端口可能会在 NET_WAIT 中徘徊...
注意:有插座是正常的 TIME_WAIT 状态长时间 的时间。时间在 RFC793 作为最大段的两倍 终生 (MSL)。 MSL 被指定为 2分钟。所以,一个套接字可能在一个 TIME_WAIT状态长达4 分钟。一些系统实现 不同的值(少于 2 分钟) 为 MSL。
这让我相信,如果每个服务调用都在服务器上打开一个新套接字,并且因为我在一个紧密的循环中调用服务,我很容易淹没服务器,导致它用完可用的套接字并进入反过来生成我上面提到的异常。
因此,我需要走以下两条路之一: 1)尝试批处理服务调用,以便它们重用服务器端套接字 2) 更改我的服务合同,以便我可以用更少的调用返回更大的数据块。
第一选择对我来说似乎更好,我将继续追求它。我会发回我的发现并欢迎更多的 cmets、问题和答案。
【问题讨论】:
-
“每个服务调用都打开了一个从代理到服务器的新套接字,并且从不关闭它们”:这真的是真的吗?
-
也许我说“永远不要关闭它们”有点强硬。如何测量应用程序打开的套接字?我现在正在运行性能监视器,以查看是否可以收集一些具体数据。我读了另一篇提到netstat的帖子。我以前没用过netstat。
-
嗯,性能监视器没有显示很多有用的东西。但是 netstat 能够在我的单元测试运行期间随着时间的推移在开放套接字中产生显着增长。单元测试完成后大约一分钟,套接字关闭。奇怪的是,netstat -b 将大多数套接字所有者归因于我的 TrendMicro 防病毒软件。
-
如果您暂时禁用防病毒软件会怎样?