3

我有一个关于 WebClient 的问题,我希望有人可以帮助我。我知道这个问题被问了几次,但我找不到一个好的答案。

我有两种不同的场景需要调用 URL。a) 我不需要来自 URL (Fire&Forget) 的任何回复。b) URL 返回一个 JSON,所以我需要等待在服务器上完成处理才能获取我的数据。

我对 a) 有一些问题,我不知道我所做的是否有意义。为了实现 Fire&Forget,我将 WebClient 超时设置为“小”数,500 毫秒(代码如下)。我的问题是:

a)这是一个好方法还是没有任何意义?b) 等待 500 毫秒是否有意义?我可以将超时设置为零吗?c) 这种方法安全吗?我不在乎完成处理需要多长时间,但我想确保我能够启动 Web 服务。下面的代码是否保证 Web 服务将被触发,如果没有,我会得到一个不是 Timneout 的异常?

非常感谢 !

public static void SubmitUrl(string url)
{
    using (WebClient webClient = new WebClientEx(500))
    {
        try
        {
            Logger.Log(string.Format("Start Invoking URL: {0}", url));
            var response = webClient.OpenRead(url);
            response.Close();
            Logger.Log(string.Format("End Invoking URL: {0}", url));

        }
        catch (System.Net.WebException ex)
        {
            if (ex.Status != WebExceptionStatus.Timeout)
            {
                Logger.Log(string.Format("Exception Invoking URL: {0} \n {1}", url, ex.ToString()));
                throw;
            }
        }
        catch (System.Exception ex)
        {
            Logger.Log(string.Format("Exception Invoking URL: {0} \n {1}", url, ex.ToString()));
            throw;
        }
    }
}

public class WebClientEx : WebClient
{
    // Timeout in milliseconds
    public int timeout { get; set; }

    public WebClientEx(int timeout)
        : base()
    {
        this.timeout = timeout;
    }

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest wr = base.GetWebRequest(address);
        wr.Timeout = this.timeout;
            return wr;
        }
    }
}
4

2 回答 2

3

将 WebClient 调用放在不同的线程中,这样它就不会阻塞主应用程序流中的任何内容。在这种情况下,Logger 必须是非常线程安全的。

    public static void SubmitUrl(string url)
    {
        Task.Factory.StartNew(() => SubmitUrlPrivate(url));
    }

    private static void SubmitUrlPrivate(string url)
    {
        using (var webClient = new WebClient())
        {
            try
            {
                Logger.Log(string.Format("Start Invoking URL: {0}", url));
                using (webClient.OpenRead(url))
                {
                    Logger.Log(string.Format("End Invoking URL: {0}", url));
                }
            }
            catch (WebException ex)
            {
                if (ex.Status != WebExceptionStatus.Timeout)
                {
                    Logger.Log(string.Format("Exception Invoking URL: {0} \n {1}", url, ex.ToString()));
                    throw;
                }
            }
            catch (Exception ex)
            {
                Logger.Log(string.Format("Exception Invoking URL: {0} \n {1}", url, ex.ToString()));
                throw;
            }
        }
    }
于 2014-08-10T05:32:03.600 回答
2

我建议您不要使用线程池线程来执行 IO 绑定工作,因为线程将大部分时间都花在阻塞 HTTP 请求上,所以根本不需要。

此外,在 ASP.NET 中分离新线程而不注册它们是危险的,并且可能导致您的线程在应用程序池回收期间意外终止。

我建议您查看HttpClient课程和使用 `async-await。

我还建议不要在大多数情况下使用火而忘记。如果请求失败或抛出异常会发生什么?它至少不应该被记录吗?

我会去做这样的事情:

private static async Task SubmitUrlAsync(string url)
{
    try
    {
        var httpClient = new HttpClient();
        Logger.Log(string.Format("Start Invoking URL: {0}", url));

        await httpClient.GetAsync(url);

        Logger.Log(string.Format("End Invoking URL: {0}", url));
    }
    catch (WebException ex)
    {
        if (ex.Status != WebExceptionStatus.Timeout)
        {
            Logger.Log(string.Format("Exception Invoking URL: 
                                {0} \n {1}", url, ex.ToString()));
            throw;
        }
    }
    catch (Exception ex)
    {
        Logger.Log(string.Format("Exception Invoking URL: 
                            {0} \n {1}", url, ex.ToString()));
        throw;
    }
}

await关键字会将线程返回给线程池以处理更多请求,直到该方法完成,并且还将传播抛出的任何异常到等待的行。

于 2014-08-10T06:33:51.980 回答