火,忘记使用WebClient设置timeout

本文关键字:WebClient 设置 timeout 忘记 | 更新日期: 2023-09-27 18:17:30

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

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

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

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

谢谢你!

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;
        }
    }
}

火,忘记使用WebClient设置timeout

将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;
            }
        }
    }

我建议您不要使用线程池线程来做io绑定的工作,因为根本没有必要,因为线程将花费大部分时间阻塞HTTP请求。

此外,在ASP中启动新线程。. NET不注册它们是危险的,可能会导致你的线程在应用程序池回收期间意外终止。

我建议你看看HttpClient类和使用' async-await。

我还建议在大多数情况下不要使用fire and forget。如果请求失败或抛出异常会发生什么?难道不应该至少记录一下吗?

我会这样写:

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关键字将使线程返回到线程池处理更多的请求,直到该方法完成,并且还将抛出任何异常到等待行。