【问题标题】:"Unable to connect to remote server fail" in HttpWebRequestHttpWebRequest 中的“无法连接到远程服务器失败”
【发布时间】:2009-10-21 04:43:11
【问题描述】:

我正在使用 VSTS 2008 + C# + .Net 3.5 开发控制台应用程序,并将请求发送到另一台服务器(Windows Server 2008 上的 IIS 7.0)。我发现当请求线程数很大(例如 2000 个线程)时,客户端在调用 response = (HttpWebResponse)request.GetResponse() 时会收到错误“无法连接到远程服务器失败”。我的困惑是——我有将超时设置为一个很大的值,但我在一分钟内收到了这样的失败消息。我认为即使连接真的比 IIS 可以服务的更大,客户端也不应该这么快收到这样的失败消息,它应该在超时后得到这样的消息。有cmets吗?有什么想法有什么问题吗?有什么想法可以让 IIS 7.0 提供更多的并发连接?

这是我的代码,

   class Program
    {
        private static int ClientCount = 2000;
        private static string TargetURL = "http://labtest/abc.wmv";
        private static int Timeout = 3600;

        static void PerformanceWorker()
        {
            Stream dataStream = null;
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            StreamReader reader = null;
            try
            {
                request = (HttpWebRequest)WebRequest.Create(TargetURL);
                request.Timeout = Timeout * 1000;
                request.Proxy = null;
                response = (HttpWebResponse)request.GetResponse();
                dataStream = response.GetResponseStream();
                reader = new StreamReader(dataStream);

                // 1 M at one time
                char[] c = new char[1000 * 10];

                while (reader.Read(c, 0, c.Length) > 0)
                {
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
            }
            finally
            {
                if (null != reader)
                {
                    reader.Close();
                }
                if (null != dataStream)
                {
                    dataStream.Close();
                }
                if (null != response)
                {
                    response.Close();
                }
            }
        }

        static void Main(string[] args)
        {
            Thread[] workers = new Thread[ClientCount];
            for (int i = 0; i < ClientCount; i++)
            {
                workers[i] = new Thread((new ThreadStart(PerformanceWorker)));
            }

            for (int i = 0; i < ClientCount; i++)
            {
                workers[i].Start();
            }

            for (int i = 0; i < ClientCount; i++)
            {
                workers[i].Join();
            }           

            return;
        }
    }

【问题讨论】:

  • 我已经更新了我的答案,你能粘贴完整的例外吗?

标签: c# .net visual-studio-2008 iis-7 httpwebrequest


【解决方案1】:

Kev 已经回答了你的问题,我只想补充一点,创建这么多线程并不是真正好的设计解决方案(只是上下文切换开销是一个很大的减号),而且它不会很好地扩展。

快速的答案是:使用异步操作来读取数据,而不是创建一堆线程。或者至少使用线程池(和较低的工作线程数)。请记住,与一个来源的更多连接只会在一定程度上加快速度。尝试对其进行基准测试,您会发现 3-5 个连接可能会比您现在使用的 2000 个更快。

您可以在此处阅读有关异步客户端/服务器架构(IOCP - 输入/输出完成端口)及其优势的更多信息。你可以从这里开始:

MSDN - Using an Asynchronous Server Socket

MSDN - Asynchronous Server Socket Example

CodeProject - Multi-threaded .NET TCP Server Examples

所有这些示例都使用较低级别的 TCP 对象,但它也可以应用于 WebRequest/WebResponse。

更新

要尝试线程池版本,您可以执行以下操作:

ManualResetEvent[] events = new ManualResetEvent[ClientCount];
for (uint cnt  = 0; cnt < events.Length; cnt++)
{
  events[cnt] = new ManualResetEvent(false);
  ThreadPool.QueueUserWorkItem(obj => PerformanceWorker());
}

WaitHandle.WaitAll(events);

未经测试,可能需要一些调整。

【讨论】:

  • 我想模拟(做性能测试)有大量并发连接数的场景。使用线程池可以比使用真正的并发线程更好地模拟这一点?
  • 你应该亲自尝试一下。我不确定在这种情况下它是否对你有好处(因为这是用于负载测试,所以对性能并不重要),但你会学到一些新东西(-。用线程池示例更新答案。
  • 我在这里有一个相关的问题,如果你能看一下,不胜感激。谢谢*.com/questions/1600002/…
【解决方案2】:

我认为您已经用尽了网站的应用程序池队列。默认值为 1000 个请求,您一次或多或少地用 2000 个请求淹没服务器。增加超时不会解决这个问题。

尝试增加站点所在应用程序池的队列长度。

您应该尝试捕获底层 HTTP 状态,这将为您提供有关实际情况的线索。

更新:

当我运行您的代码并尝试下载一个相当大的文件 (200MB) 时,我得到(503) Server Unavailable.。增加应用程序池的请求队列的大小可以解决这个问题(我设置为 10000)。

我只见过一次Unable to connect to remote server,遗憾的是无法复制。这个错误听起来好像 TCP/IP 层有问题。你能发布完整的例外吗?

【讨论】:

  • 谢谢! 1.我已将其增加到65535。我认为您的意思是IIS Manager => Application pools => Advanced Settings => Queue Length。这是您要设置的正确位置和值吗? 2、底层Http状态如何获取?
  • 就像我在 iis.net 论坛上评论的那样,HTTP 错误日志包含该信息。
  • 感谢 Kev,“应用程序池的请求队列解决了这个问题”,您的意思是 IIS 管理器 => 应用程序池 => 高级设置 => 队列长度?
  • 另一个问题是,你使用的并发客户端是什么? 2000 年?
  • @George -是的,IIS 管理器 => 应用程序池 => 高级设置 => 队列长度是我的意思。我在 ClientCount = 2000 的情况下运行。您使用什么节流率设置?
【解决方案3】:

转到Smart Thread Pool 并下载代码。它是一个限制线程数量的实例线程池。 .Net 线程池在连接到 Web 服务器和 SQL 服务器的应用程序中可能会出现问题。

把循环改成这个

static void Main(string[] args)
        {
            var stp = new SmartThreadPool((int) TimeSpan.FromMinutes(5).TotalMilliseconds,
                                          Environment.ProcessorCount - 1, Environment.ProcessorCount - 1);
            stp.Start();
            for (var i = 0; i < ClientCount; i++)
            {
                stp.QueueWorkItem(PerformanceWorker);
            }
            stp.WaitForIdle();
            stp.Shutdown();

            return;
        }

这将线程池限制为每个 proc 使用 1 个线程。调整它直到性能开始下降。太多的线程比太少更糟糕。你们很多人发现这是最优的。

也将此添加到您的配置中。我使用的默认值是 100。有一种方法可以在代码中执行此操作,但语法现在让我无法理解。

<system.net>

    <connectionManagement>

        <add address=“*“ maxconnection=“100“ />

    </connectionManagement>

</system.net>

【讨论】:

  • 智能线程池打不开。我在网上搜了几个,你说的是这个吗? codeproject.com/KB/threads/smartthreadpool.aspx
  • 为什么“.Net 线程池在连接到 Web 服务器和 SQL 服务器的应用程序中可能会出现问题。”?
【解决方案4】:

我使用的是 Visual Studio 2005。如何发送短信,这是我的代码:

IPHostEntry host;
host = Dns.GetHostEntry(Dns.GetHostName());
UriBuilder urlbuilder = new UriBuilder();
urlbuilder.Host = host.HostName;
urlbuilder.Port = 4719;
string PhoneNumber = "9655336272";
string message = "Just a simple text";
string subject = "MMS subject";
string YourChoiceofName = "victoria";
urlbuilder.Query = string.Format("PhoneNumber=%2B" + PhoneNumber + "&MMSFrom=" + YourChoiceofName + "&MMSSubject=" + subject + "&MMSText=" + message);//+ "&MMSFile=http://127.0.0.1/" + fileName
HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(urlbuilder.ToString(), false));
HttpWebResponse httpResponse = (HttpWebResponse)(httpReq.GetResponse());

【讨论】: