火与;通过设置Timeout忘记使用WebClient

时间:2014-08-10 02:30:10

标签: c# webclient fire-and-forget

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

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

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

a)这是一个好方法还是毫无意义? b)等待500ms甚至有意义吗?我可以将超时设置为零吗? 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;
        }
    }
}

2 个答案:

答案 0 :(得分: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;
            }
        }
    }

答案 1 :(得分:2)

我建议你再次使用线程池线程进行IO绑定工作,根本没有必要,因为线程将花费大部分时间来阻止http请求。

更重要的是,在没有注册yhem的情况下剥离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关键字将使线程返回到线程池以处理更多请求,直到方法完成,并且还将传播抛出到等待线路的任何异常。