【问题标题】:Request doesn't wait for response in .net请求不等待 .net 中的响应
【发布时间】:2014-08-21 10:20:03
【问题描述】:

我有一个 Windows 服务,它正在将文件上传到正在处理它们的其他网站。问题是,对于小文件,它可以正常工作并且从那里得到响应,但是对于大文件(大约需要 6 分钟的处理时间),它会永远处于等待模式。

这是外部网站发布方法代码的一部分:

try
{
  ...
  LogResults();
  return string.Empty;
}
catch (Exception e)
{
  return e.Message;
}

问题是我甚至可以看到大文件的日志,所以这意味着网站总是返回值,但对于大文件,我的 Windows 服务不会等待它们。

这是来自 Windows 服务的代码

var valuesp = new NameValueCollection
{
     { "AccountId", datafeed.AccountId }
};

byte[] resultp = UploadHelper.UploadFiles(url, uploadFiles, valuesp);
response = Encoding.Default.GetString(resultp);

UploadFiles 方法为小文件返回值,但对于大文件永远等待。

这是 UploadFiles 的完整代码

public static byte[] UploadFiles(string address, IEnumerable<UploadFile> files, NameValueCollection values)
{
    var request = WebRequest.Create(address);
    request.Timeout = System.Threading.Timeout.Infinite; //3600000; // 60 minutes
    request.Method = "POST";
    var boundary = "---------------------------" +
                   DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
    request.ContentType = "multipart/form-data; boundary=" + boundary;
    boundary = "--" + boundary;

    using (var requestStream = request.GetRequestStream())
    {
        // Write the values
        if (values != null)
        {
            foreach (string name in values.Keys)
            {
                var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
                buffer =
                    Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{1}", name,
                                                          Environment.NewLine));
                requestStream.Write(buffer, 0, buffer.Length);
                buffer = Encoding.UTF8.GetBytes(values[name] + Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
            }
        }

        // Write the files
        if (files != null)
        {
            foreach (var file in files)
            {
                var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
                buffer =
                    Encoding.UTF8.GetBytes(
                        string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}", file.Name,
                                      file.Filename, Environment.NewLine));
                requestStream.Write(buffer, 0, buffer.Length);
                buffer =
                    Encoding.ASCII.GetBytes(string.Format("Content-Type: {0}{1}{1}", file.ContentType,
                                                          Environment.NewLine));
                requestStream.Write(buffer, 0, buffer.Length);
                requestStream.Write(file.Stream, 0, file.Stream.Length);
                buffer = Encoding.ASCII.GetBytes(Environment.NewLine);
                requestStream.Write(buffer, 0, buffer.Length);
            }
        }

        var boundaryBuffer = Encoding.ASCII.GetBytes(boundary + "--");
        requestStream.Write(boundaryBuffer, 0, boundaryBuffer.Length);
    }

    using (var response = request.GetResponse())
    using (var responseStream = response.GetResponseStream())
    using (var stream = new MemoryStream())
    {
        responseStream.CopyTo(stream);
        return stream.ToArray();
    }
}

我在这里做错了什么?

编辑:在本地它甚至可以处理 7-8 分钟。但在现场环境中没有。它可以与主应用程序 IIS 设置相关吗?会不会和windows服务服务器设置有关?

编辑 2: 远程服务器 web.config httpRuntime 设置

<httpRuntime enableVersionHeader="false" maxRequestLength="300000" executionTimeout="12000" targetFramework="4.5" />

【问题讨论】:

  • 我看不到任何明显的东西,但值得注意的是,仅仅因为您的 Web 层收到了请求,并不意味着您的客户端收到了响应。如果是我,我会得到一份 Fiddler 的副本,然后只观察 HTTP 流量来回移动,看看您是否可以确定响应是从 Web 服务器返回的(并且没有被 .net 发现)或者响应的位置永远不会从 Web 服务器返回。希望更敏锐的眼睛会发现代码问题,但 Fiddler 将是我的第一个诊断。
  • 如果你使用StreamReader而不是将流复制到内存流会发生什么?像这样:StreamReader reader = new StreamReader(responseStream); string responseFromServer = reader.ReadToEnd();
  • 相同的结果...实际上响应是非常小的字符串,所以我不认为 MemoryStream 是问题。
  • 小编辑已添加到问题中。
  • 我在发布服务端看不到任何错误处理。如果using (var response = request.GetResponse()) 抛出异常(例如超时)会发生什么?你会在某个地方看到它吗?我想要的是,您真的确定服务实际上是在等待响应吗?

标签: c# .net request response webrequest


【解决方案1】:

问题出在 Azure 负载均衡器上,默认情况下空闲超时设置为 4 分钟。 Windows Azure 博客的新帖子说此超时现在可配置为 4-30 分钟之间的值

http://azure.microsoft.com/blog/2014/08/14/new-configurable-idle-timeout-for-azure-load-balancer/

但是,通过 TCP 发送额外的 KeepAlive 字节解决了问题,这告诉负载均衡器不要终止请求。

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address);
...
request.Proxy = null;
request.ServicePoint.SetTcpKeepAlive(true, 30000, 5000); //after 30 seconds, each 5 second

将 Proxy 设置为 null(不使用代理)是此处的强制操作,因为否则代理将不会将 tcp 字节传递给服务器。

【讨论】:

  • 终于!这解决了我长期存在的问题。我有一个长时间运行的请求,在使用 HttpClient.GetAsync 或 WebRequest.GetResponseAsync 时一直在等待。即使服务器表明它已经完成了它的请求,但请求从未完成。事实证明,当防火墙终止连接时会发生这种情况。 Http Keepalive 和 Tcp keepalive 之间的搜索词混淆也无济于事。 en.wikipedia.org/wiki/Keepalive#HTTP_keepalive
【解决方案2】:

使用符合 RFC1867 的方法上传文件

See this

然后:

 UploadFile[] files = new UploadFile[]
{
new UploadFile(fileName1),
new UploadFile(fileName2)
};

NameValueCollection form = new NameValueCollection();

form["name1"] = "value1";
form["name2"] = "xyzzy";

string response = UploadHelper.Upload(url, files, form);

就是这样!

编辑:

我用上面的方法上传100MB以上的文件,我根本不用ASP,简直完美!

【讨论】:

    【解决方案3】:

    我有一个类似的问题,上传可以在本地工作,但在服务器上超时。这里有两件事在起作用——我假设你在谈论 IIS7+。如果没有,请告诉我,我很乐意删除我的答案。

    首先,有 ASP.NET 会查看此设置(在 system.web 下):

    <!-- maxRequestLength is in KB - 10 MB here. This is needed by ASP.NET. Must match with maxAllowedContentLength under system.webserver/security/requestLimits -->
    <httpRuntime targetFramework="4.5" maxRequestLength="1048576" />
    

    然后是 IIS:

    <system.webServer>
        <security>
            <requestFiltering>
                <!-- maxAllowedContentLength in bytes - 10MB -->
                <requestLimits maxAllowedContentLength="10485760" />
            </requestFiltering>
        </security>
    <system.webServer>
    

    您需要同时存在这两个设置以及匹配的限制才能使事情正常工作。请注意,一个以 KB 为单位,另一个以字节为单位。

    【讨论】:

      【解决方案4】:

      您是否检查了远程服务器上的 IIS 文件大小限制?它默认为 30MB,因此如果您尝试上传的文件大于此大小,它将失败。以下是更改上传限制 (IIS7) 的方法:http://www.web-site-scripts.com/knowledge-base/article/AA-00696/0/Increasing-maximum-allowed-size-for-uploads-on-IIS7.html

      【讨论】:

      • 文件的最大大小为 3-4 MB。
      • 无论如何都要检查一下...我知道一些老(学校)系统管理员的“默认”是 500k!
      【解决方案5】:

      您可以使用 AsyncCallback 技术完成此任务。

      什么是 AsyncCallback?

      async 方法处理完毕后,会自动调用 AsyncCallback 方法,执行后处理 stmts。使用这种技术,无需轮询或等待异步线程完成。

      您可以从此链接找到更多详细信息

      AsynCallback

      【讨论】:

      • 没有区别,同步/异步两种情况下都不会响应。
      【解决方案6】:

      可能是IIS上传限制造成的? 在 IIS7 中,标准值为 30MB。见MSDN

      编辑1: 请注意,如果您在 1 个请求中上传多个文件,则大小会增加。

      编辑2: 在所有 MS 示例中,始终只有一个 Stream.Write()。 在 MSDN 中是这样规定的:Stream 对象返回后,您可以使用 Stream.Write 方法通过 HttpWebRequest 发送数据。我对这句话的解释是,您应该调用 只写一次()。将要发送的所有数据放入缓冲区,然后调用 Write()。

      【讨论】:

      • 不,文件只有 1.5mb。此外,从 web.config 增加了执行超时和最大请求​​长度。
      • 这是一次上传的所有组合文件的大小(我认为您使用 IEnumerable 时还有更多文件)还是一个文件的大小?您一次上传多少个文件?
      • 不,UploadHelper 允许上传多个文件,但我一直只上传一个。
      【解决方案7】:

      您应该将这个 executionTimeout="12000" 更改为 executionTimeout="360000" 这意味着将执行超时从 2 分钟更改为 6 分钟。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-24
      • 1970-01-01
      • 1970-01-01
      • 2011-01-12
      • 2015-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多