.NET Web服务调用池化?

4
我正在编写验收测试来验证系统,其数据访问全部通过web服务调用完成。
不幸的是,因为我快速地进行了很多调用,所以遇到了以下错误:
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted
我已经尽可能地在本地缓存了尽可能多的调用,但这还不够。我无法伪造或模拟这些调用。它们必须是真实的,才能使这一级别的测试有效。
有没有办法将频繁的web服务调用重复使用池?
或者在尝试调用之前检查是否可以连接到服务,并等待可用套接字存在?
在生产中,这些调用从未以这种速度进行,因此问题不会出现...但是,有些代码可以允许测试工作,并限制或共享连接,这将是很好的。
谢谢

你是在使用WCF还是旧的ASMX服务?你是如何进行调用的?你是如何进行缓存的? - John Saunders
1
你是在远程服务器上运行这个测试还是在托管服务的 Web 服务器上运行呢?请确保您远程运行这些测试。 - jwmiller5
@John,它们是旧的 ASMX。缓存位于进行调用的方法上,简而言之,它们跟踪调用并将结果保存在内存中,而不是使用相同参数再次调用 Web 服务,它会返回内存中的对象。 - CaffGeek
@John,是的,这是错过的累积。如果没有缓存,问题会在大约第5个测试时出现,而有了缓存,现在在第35个测试左右出现。 - CaffGeek
@Chad:我的下一个问题是你的测试是否有效。你会从单个客户端那么快地建立如此多的连接吗?如果不是,也许你应该使用多台客户端计算机来执行测试。 - John Saunders
显示剩余3条评论
3个回答

2
原来,一位同事已经在我们的电子商务网站上使用了一个解决方案。你只需要继承自你的 Web 服务,在 GetWebRequest 函数中覆盖一些不同的设置,然后就完成了。
class SafeMyWebService : MyWS.Service
{
    private static int _lastBindPortUsed;

    protected override WebRequest GetWebRequest(Uri uri)
    {
        var webreq = base.GetWebRequest(uri) as HttpWebRequest;

        webreq.KeepAlive = true;
        webreq.ProtocolVersion = HttpVersion.Version10;
        webreq.ServicePoint.MaxIdleTime = 10 * 1000; // milliseconds 
        webreq.ServicePoint.BindIPEndPointDelegate = new BindIPEndPoint(BindIPEndPointCallback);
        webreq.Timeout = 2000;
        webreq.ConnectionGroupName = "MyConnectionGroupName";

        return webreq;
    }

    public static IPEndPoint BindIPEndPointCallback(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount)
    {
        int port = Interlocked.Increment(ref _lastBindPortUsed);
        Interlocked.CompareExchange(ref _lastBindPortUsed, 5001, 65534);

        return remoteEndPoint.AddressFamily == AddressFamily.InterNetwork 
            ? new IPEndPoint(IPAddress.Any, port) 
            : new IPEndPoint(IPAddress.IPv6Any, port);
    }

}

我猜它使用更多资源,但它不会失败。

1

MSDN文章避免TCP/IP端口耗尽建议采用以下方法:

  • 通过注册表增加可用的短暂端口数量(即客户端分配的用于调用Web服务的端口)
  • 减少TCP/IP超时以更快地释放端口(同样通过注册表)。

在测试的拆卸中,您还应检查是否正确关闭了每个调用后的连接。

或者,如果您不想进行任何配置更改,您可以扫描短暂端口范围,如果没有空闲端口,则暂停并等待一些端口被释放。文章“检测下一个可用的空闲TCP端口”有一些示例代码,可以为此目的进行调整。


Web服务调用正在使用“using”语句,因此它会正确关闭和处理。问题在于Windows不会立即关闭套接字(超时)。因此,即使您关闭了它,它实际上并没有关闭,而且它们变得耗尽。我宁愿不进行系统更改,因为这些测试应该能够在任何地方运行...而更改系统设置以增加短暂端口或减少超时只会延迟问题,而不是解决问题。 - CaffGeek
@Chad:添加了一个建议,建议您扫描临时范围以检查是否有任何可用端口(并包含一些示例代码的链接)。这应该让您在不需要使用异常的情况下退出测试。 - Robert Christie

0
一个简单的解决方案是通过让线程短暂休眠来减缓测试速度;比生产环境快但比使用所有端口的时间短。
应用程序或系统不做任何更改。

@Jim,我已经实现了这个功能。但是方式不太理想。我用do/while包装了调用。如果抛出SocketException异常,我会将标志“hasSocketException”设置为true,并循环直到它再次变为false。在每次尝试调用之前,都会将其设置为false。然而,我更喜欢一种不使用异常处理来进行流程控制的解决方案。 - CaffGeek
这样会更清晰:System.Threading.Thread.Sleep(); 你也可以串行执行测试,而不是并行执行(不确定如何实现)。 - Jim
@Jim,这些测试是串行的。哦,当发生异常时会有休眠操作。忘记提到了。 - CaffGeek
每次测试后,我会让线程休眠。如果遇到异常,增加更多的休眠时间。你需要猜测一个值,它不会引起异常,但可以让测试在合理的时间内完成。你真的遇到了系统架构限制。池化将解决这个问题,但如果你的系统在生产中永远不会接近那么大的负载,则不必要。等待空闲端口也可以解决问题。 - Jim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接