Запустите 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;
}
}
}