Подтвердить что ты не робот

Async HttpWebRequest без ожидания из веб-приложения

В моем веб-приложении (ASP.NET) у меня есть блок кода, который использует HttpWebRequest для вызова службы REST и продолжения выполнения. Сейчас это занимает больше времени, чем я хотел бы заполнить полный веб-запрос. Дело в том, что возвращение службы REST нецелесообразно. В идеале я бы хотел отправить веб-запрос Async в службу REST, а затем НЕ ждать ответа. Проблема в том, что я пробовал это с помощью

request.BeginGetResponse(New AsyncCallback(AddressOf myFunc), Nothing)

Чтобы запустить асинхронный запрос и вместо НЕ ждать (я бы предположил, что это будет поведение по умолчанию для асинхронного запроса), он непрерывно выполняет функцию обратного вызова перед выполнением следующей строки кода после BeginGetResponse.

Я подозреваю, что ASP.NET может преобразовать его в запрос синхронизации, когда он находится в веб-приложении. Мне повезло, потому что там есть объект IAsyncResult result, который передается в функцию обратного вызова, и когда я исследую его свойство CompletedSynchronously, он всегда устанавливается в true.

Кто-нибудь знает, возможно ли выполнить async HttpWebRequest (без ожидания) из веб-приложения ASP.NET или он всегда преобразуется в синхронный запрос?

4b9b3361

Ответ 2

HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(myUrl);
//set up web request...
ThreadPool.QueueUserWorkItem(o=>{ myRequest.GetResponse(); });

Также известен как Fire-and-Forget.

Ответ 3

(Примечание: я начал вводить это в качестве комментария, но по мере того как я приобрел некоторые дополнительные знания во время исследования, он стал достаточно большим для ответа)

1. ThreadPool.QueueUserWorkItem

Первые две ссылки, которые предоставляет @ram, а также @Bryan Batchelders, используют противоречивый ThreadPool.QueueUserWorkItem. Это противоречиво в контексте ASP.NET, потому что неактивное использование может голодать пул потоков, как показывает @Perhentian .

2. BeginInvoke

Затем я посмотрел на @ram третью ссылку, которая использует BeginInvoke. По сути, это просто похоже на некоторый код для для другого потока. Таким образом, здесь нет разрешения.

3. BeginGetResponse

Теперь вернемся к ссылке @Perhentians . В нем указано, как BeginGetResponse несколько отличается от реального потока, поскольку использует IO Completion Ports (IOPCs). Так что вы действительно ищете, это решение, которое все еще использует эти IOPC без создания дополнительного потока.

Теперь, чтобы увидеть, как это делает .NET, я попытался вникнуть в HttpWebRequest.BeginGetResponse, что на самом деле является трюмом внутренних вызовов. Он выглядит следующим образом:

  • HttpWebRequest.BeginGetResponse
  • ServicePoint.SubmitRequest
  • Connection.SubmitRequest
  • Два варианта:
    • Если соединение ясное: Connection.StartRequest
    • В противном случае: Connection.WaitList.Add(запрос)

A. Лист ожидания

Сначала рассмотрим вариант 2: указанный WaitList обрабатывается, когда соединение выполняется с предыдущим запросом. Взглянув на ConnectStream, задействованный во всей цепочке, вы увидите ThreadPool.QueueUserWorkItem с замечанием:

// otherwise we queue a work item to parse the chunk
// Consider: Will we have an issue of thread dying off
// if we make a IO Read from that thread???

Итак, по крайней мере, в некоторых сценариях резервного копирования потоки могут по-прежнему непреднамеренно генерироваться инфраструктурой, просто используя BeginGetResponse!

В. Connection.StartRequest

Теперь у нас все еще есть сценарий, где соединение ясное. Обратный вызов устанавливается System.Net.Sockets.Socket.BeginConnect и фактически вызывается BeginAccept. Еще некоторое копание показывает вызов ThreadPool.UnsafeRegisterWaitForSingleObject, результат которого используется для ожидания.

Заключение

Наконец, мы можем сказать, что на самом деле происходит при выполнении BeginGetResponse:

// 1. Connecting a socket
UnsafeNclNativeMethods.OSSOCK.WSAConnect(m_handle)

// 2. Registering the callback
m_RegisteredWait = ThreadPool.UnsafeRegisterWaitForSingleObject(m_AsyncEvent, s_RegisteredWaitCallback, this, Timeout.Infinite, true);

// 3. Waiting for the socket to complete
UnsafeNclNativeMethods.OSSOCK.WSAEventSelect(m_handle, m_AsyncEvent.SafeWaitHandle, blockEventBits);

Обратите внимание на Timeout.Infinite. Я все еще изучаю, можно ли получить сокет, запускающий кодовую страницу, которая не выполняет ожидание, но пока это мне кажется невозможным.

Как и для моего заключения: похоже, нет простого способа использовать IOCP в сценарии Fire-And-Forget. Поэтому, если вы не можете заставить вышеупомянутый сокет не дождаться завершения на BeginAccept, вы застряли в ThreadPool.