Search code examples
c#webclientfire-and-forget

Fire & Forget using WebClient by setting Tmeout


I have a question regarding the WebClient and I was hoping someone could help me. I know this question was asked a few times but I could not find a good answer.

I have two different scenarios where I need to invoke an URL. a) I don’t need any response back from the URL (Fire&Forget). b) The URL returns a JSON so I need to wait for the processing to be done on the server in order to get my data.

I have some issues with a) and I have no idea if what I have done makes sense. In order to implement the Fire&Forget, I’m setting the WebClient timeout to a “small” number, 500 milliseconds ( the code is below). My questions are :

a) Is this a good approach or makes no sense whatsoever? b) Does it even make sense to wait for 500ms? Can I just set my timeout to zero? c) Is this approach safe? I don’t care how long it takes for the processing to be done but I want to make sure that I was able fire the web service. Does the code below guarantee that the web service will be fired and if not, i will get an exception that is not a Timneout?

Thanks a lot !

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

Solution

  • I would advise you against using a thread pool thread to do IO-bound work, there simply isn't a need as the thread will spend most of its time blocking on the HTTP request.

    Moreover, spinning off new threads inside ASP.NET without registering them is dangerous and may cause your thread to be terminated unexpectedly during an app pool recycle.

    I do advise you into looking at the HttpClient class and the use of `async-await.

    I would also advise against using fire and forget in most scenarios. What happened if the request fails or throws an exception? Shouldn't it be at least logged?

    I would go for something like this:

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

    The await keyword will yield the thread back to the threadpool to process more requests untill the method is done and will also propogate any exception thrown to the awaited line.