example establecer create crear consume c# sql-server web-services architecture timeout

establecer - web service rest json c#



GestiĆ³n de tiempos de espera de servicio web al realizar tareas de base de datos de larga ejecuciĆ³n (4)

La arquitectura de uno de nuestros productos es una solución típica de 3 niveles:

  • C # cliente
  • Servicio web WCF
  • Base de datos SQL Server

El cliente solicita información del servicio web. El servicio web accede a la base de datos para obtener la información y la devuelve al cliente.

Este es el problema. Algunas de estas consultas pueden llevar mucho, mucho tiempo, y no sabemos de antemano cuáles serán lentas. Sabemos que algunas son a menudo más lentas que otras, pero incluso las solicitudes más simples pueden ser lentas dado datos suficientes. A veces utiliza consultas o ejecuta informes en grandes cantidades de datos. Las consultas se pueden optimizar solo hasta el momento antes de que el volumen total de datos las ralentice.

Si una consulta en la base de datos alcanza el máximo tiempo de espera de consulta en el servidor SQL, la consulta de la base de datos finaliza y el servicio web devuelve un error al cliente. Esto es entendido Podemos manejar estos errores.

El cliente está esperando que se complete la llamada al servicio web. Si la llamada a la base de datos tarda mucho tiempo, el cliente puede agotar el tiempo de espera en su llamada al servicio web. El cliente se da por vencido, pero la solicitud de la base de datos continúa el proceso. En este punto, el cliente no está sincronizado con la base de datos. La llamada a la base de datos puede tener éxito o no. Puede haber habido un error. El cliente nunca lo sabrá. En algunos casos, no deseamos que nuestros usuarios inicien otra solicitud que pueda dar como resultado un estado no válido dada la finalización de la solicitud anterior.

Tengo curiosidad por ver cómo otros han manejado este problema. ¿Qué estrategias ha utilizado para evitar que los tiempos de espera del servicio web afecten las llamadas a la base de datos?

Las mejores ideas que se me ocurren son hacer una capa de base de datos real en alguna parte, dentro del servicio web, adjunta a una cola de mensajes, algo. La descarga de cada consulta a otro proceso parece excesiva. (Por otra parte, no siempre sabemos si una solicitud determinada será rápida o lenta).

Sería genial si pudiéramos separar el acto de realizar una solicitud HTTP a partir del acto de iniciar y ejecutar un proceso de base de datos. He visto esto hecho con un servidor personalizado en una compañía anterior, pero estaba usando una comunicación directa de socket, y prefiero evitar reemplazar el servicio web con alguna aplicación personalizada.

Tenga en cuenta que dada la cantidad de datos que manejamos, estamos en la optimización de consultas. La optimización de consultas, los índices, etc., solo lo lleva tan lejos cuando el volumen de datos es alto. A veces las cosas solo toman mucho tiempo.


El servicio web podría ejecutar las consultas en un threadpool y si el hilo no termina dentro, digamos 5 segundos (ver Thread.Join ()), la llamada al servicio web le devuelve al cliente un ID de trabajo en lugar del conjunto de resultados que el cliente puede entonces use para sondear el servidor cada pocos segundos para ver si su consulta finalizó. Cuando termina un hilo, los resultados se pueden almacenar en una tabla hash hasta que el cliente vuelva a sondear.


He encontrado problemas similares en el pasado y usé uno de los siguientes 3 métodos para resolverlo:

  1. Agregue todas las consultas de larga ejecución a una cola y trátelas secuencialmente.
    En mi caso, todos estos eran informes complicados que luego se enviaban por correo electrónico al cliente, o que se almacenaban en tablas permanentes "temporales", para que los vean los clientes después de haber sido notificados.
  2. Llamamos a un servicio web utilizando una llamada JQuery, que luego llamó a un método de devolución de JavaScript cuando se completó.
    Esto funcionó bien cuando no queríamos que la carga de la página se sincronizara con lo que estaba haciendo el servicio web.
    Sin embargo, eso significaba que esa pieza de funcionalidad no estaba disponible hasta que se completó el proceso de larga ejecución.
  3. El más complicado.
    Aparece otra ventana que muestra una barra de progreso, que también sondea el servidor periódicamente.
    Esto usó una variable de sesión para determinar qué tan lejos mostrar la barra de progreso.
    Después de que se inició la barra de progreso, se inició un nuevo subproceso que actualizaba periódicamente la misma variable de sesión.
    Una vez que el valor de la variable de sesión se estableció en 100, la ventana emergente se cerró sola.
    A los clientes les encantó este método.

De todos modos, espero que uno de esos sea de alguna ayuda para ti.


Una de las soluciones que hemos utilizado últimamente es dividir los enormes procesos de bases de datos en operaciones paralelas separadas. Cada operación es mucho más pequeña y está diseñada para ser lo más eficiente posible. Los clientes inician las operaciones, engendran algunos hilos y hacen lo que pueden en paralelo.

Por ejemplo, hemos dividido algunos procesos enormes en una serie de pasos como Inicio, Procesar 1 fragmento de trabajo, Terminar y Recopilar datos de informe. Los pasos de trabajo de proceso pueden ejecutarse en paralelo, pero no pueden comenzar hasta que se complete el paso de inicio. El paso Finalizar debe esperar a que se completen todos los pasos del Proceso de trabajo.

Como el cliente está controlando el proceso, el cliente puede informar el progreso exactamente en qué paso está.


Romper el problema en pequeños trozos es sin duda una buena idea.

Además de eso y de lo que otros dijeron (y solo si tienes la mano en la implementación del servicio web), he estado utilizando las URL de devolución de llamada que se han pasado al servicio web. El WS debe llamarlo con errores o resultados en la cadena de consulta o datos de publicación.

Las URL generalmente contienen un token que sirve para permitir que la devolución de llamada vuelva a ingresar al cliente, y que se asigna a cualquier información relevante necesaria para realizar acciones después de que se recibe la devolución de llamada (almacenada en una base de datos o en la memoria).

Es un poco pesado (especialmente si ya no se está ejecutando en un servidor web), pero garantiza un viaje de ida y vuelta exitoso en caso de que el cliente agote el tiempo de espera, pero el servicio web recibió las instrucciones correctamente y su procesamiento es lento.

Una vez que esto está configurado, su servicio web está realmente más cerca de estar listo para ejecutarse de forma asíncrona y responder rápidamente al cliente: por lo general, hacer cualquier comprobación para responder si está bien o no, y engendrar la operación lenta en un ciclo separado, con la url de devolución de llamada para que pueda informar al cliente.

No estoy seguro de cuán ortodoxo es esto, por cierto, pero resolvió problemas reales.