Запустите Fire & Forget, используя WebClient, установив Tmeout

У меня есть вопрос, касающийся WebClient, и я надеялся, что кто-нибудь сможет мне помочь. Я знаю, что этот вопрос задавали несколько раз, но я не мог найти хороший ответ.

У меня есть два разных сценария, когда мне нужно вызвать URL. а) Мне не нужен ответ от URL (Fire&Forget). б) URL возвращает JSON, поэтому мне нужно дождаться обработки на сервере, чтобы получить мои данные.

У меня есть некоторые проблемы с а), и я понятия не имею, имеет ли смысл то, что я сделал. Чтобы реализовать Fire&Forget, я устанавливаю таймаут WebClient на "маленькое" число, 500 миллисекунд (код ниже). Мои вопросы:

а) Это хороший подход или не имеет никакого смысла? б) Есть ли смысл ждать 500 мс? Могу ли я просто установить тайм-аут на ноль? в) Безопасен ли этот подход? Мне все равно, сколько времени займет обработка, но я хочу убедиться, что смог запустить веб-сервис. Гарантирует ли приведенный ниже код, что веб-служба будет запущена, и если нет, я получу исключение, которое не является 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 ответа

Решение

Я бы посоветовал вам снова не использовать поток пула потоков для выполнения работы, связанной с вводом-выводом, просто в этом нет необходимости, так как поток будет проводить большую часть своего времени, блокируя запрос http.

Более того, выделение новых потоков внутри ASP.NET без регистрации yhem опасно и может привести к неожиданному завершению потока во время перезапуска пула приложений.

Я советую вам посмотреть на 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 Ключевое слово вернет поток обратно в пул потоков для обработки большего количества запросов, пока метод не будет завершен, а также распространит любое исключение, выданное ожидаемой строке.

Поместите вызов 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;
            }
        }
    }
Другие вопросы по тегам