net example await async asp asp.net asynchronous asp.net-web-api async-await httpcontext

asp.net - example - task async



Servicio web API: cómo usar "HttpContext.Current" dentro de la tarea asincrónica (2)

Desde el principio:

public async Task<object> Post([FromBody]string data) { object response = ExecuteServerLogics(data); return response; }

No ignore las advertencias del compilador; el compilador generará una advertencia para este método que indica específicamente que se ejecutará sincrónicamente.

Continuando:

en algunas de las llamadas de los clientes, experimentamos problemas de rendimiento.

El código asíncrono en el servidor no será más rápido para una sola llamada en forma aislada. Solo te ayuda a escalar tu servidor.

En particular, Task.Run todos los beneficios de rendimiento de async y luego degradará el rendimiento un poco más allá. Creo que la mejora en el rendimiento que midió fue una coincidencia.

en la mayoría de las publicaciones encontramos que deberíamos pasar la referencia HttpContext del subproceso de trabajo en la tarea interna que ejecuta la lógica del servidor.

Esas publicaciones son incorrectas. EN MI HUMILDE OPINIÓN. HttpContext utilizando el objeto HttpContext de una HttpContext de fondo, cuando ese objeto está específicamente diseñado para que solo se pueda acceder desde una cadena de solicitud.

¿Estoy haciendo todo el diseño mal de alguna manera?

Te recomiendo que retrocedas un momento y pienses en el panorama general. Cuando entra una solicitud, tiene una cierta cantidad de trabajo por hacer. Si el trabajo se realiza de forma síncrona o asíncrona es irrelevante para el cliente; ambos enfoques tomarán aproximadamente la misma cantidad de tiempo.

Si necesita regresar temprano al cliente, necesitará una arquitectura completamente diferente. El enfoque habitual es poner el trabajo en fila en una cola confiable (p. Ej., Cola Azure), tener un backend separado (p. Ej., Azure WebRole) y notificar al cliente de forma proactiva cuando se complete el trabajo (p. Ej., SignalR).

Sin embargo, eso no quiere decir que la async sea ​​inútil. Si ExecuteServerLogics es un método de E / S enlazado, se debe hacer asíncrono en lugar de bloquear, y luego puede usar métodos asíncronos como tales:

public async Task<object> Post([FromBody]string data) { object response = await ExecuteServerLogicsAsync(data); return response; }

Esto permitirá que su servidor sea más receptivo y escalable en general (es decir, no se sienta abrumado por muchas solicitudes).

Estoy utilizando un método asíncrono "Publicar" del servicio de descanso webApi:

public async Task<object> Post([FromBody]string data) { object response = ExecuteServerLogics(data); return response; }

Este código anterior funcionó bien, pero en algunas de las llamadas de los clientes, experimentamos problemas de rendimiento.

Después de leer algunos artículos aquí, me he dado cuenta de que nuestro servicio de descanso webApi no funciona realmente de manera asíncrona con sus solicitudes web entrantes, ya que olvidamos utilizar el patrón async / await :

public async Task<object> Post([FromBody]string data) { object response = await Task<object>.Run( () => { return ExecuteServerLogics(data); }); return response; }

Después de este arreglo notamos que el rendimiento mejoró, pero encontramos otro problema crítico: al acceder a HttpContext.Current - devuelve una referencia nula:

public async Task<object> Post([FromBody]string data) { object response = await Task<object>.Run( () => { var currentContext = HttpContext.Current; // Returns Null! return ExecuteServerLogics(data); }); return response; }

Intentamos encontrar una solución para ello, y en la mayoría de las publicaciones descubrimos que deberíamos pasar la referencia HttpContext del subproceso de trabajo a la tarea interna que ejecuta la lógica del servidor. El problema con esta solución es que los métodos de lógica del servidor utilizan muchas clases estáticas que usan "HttpContext.Current" , como -

  1. Llamadas de registradores.
  2. clases de seguridad estáticas que recuperan el usuario.identidad
  3. clases de seguridad estáticas que recupera los datos de sesión de la solicitud entrante, etc.

Por lo tanto, pasar la referencia "HttpContext.Current" del subproceso de trabajo no lo resolverá.

Cuando probamos la siguiente solución:

public async Task<object> Post([FromBody]string data) { // Save worker context: var currentContext = HttpContext.Current; object response = await Task<object>.Run( () => { // Set the context of the current task : HttpContext.Current = currentContext ; // Causes the calls not to work asynchronously for some reason! // Executes logics for current request: return ExecuteServerLogics(data); }); return response; }

por alguna razón, notamos que el rendimiento empeoró nuevamente, como si hubiera vuelto a funcionar sincrónicamente otra vez.

Nuestros problemas son:

1. ¿Por qué en el último ejemplo, al establecer "HttpContext.Current" dentro de la tarea de espera, hace que las solicitudes devuelvan los mismos resultados de rendimiento incorrecto que similares a los resultados sincrónicos ?

2. ¿Hay alguna otra forma de que podamos usar "HttpContext.Current" dentro de la tarea interna que llama - "ExecuteServerLogics", y en todas las clases estáticas que también llaman "HttpContext.Current" ? ¿Estoy haciendo todo el diseño mal de alguna manera?

¡Gracias!


Si su tarea está dentro de su clase derivada de ApiController, puede usar:

var ctx = this.Request.Properties["MS_HttpContext"] as System.Web.HttpContextWrapper;

Esto le dará un contenedor HttpContext con todas las propiedades habituales.