2012-02-21 13 views
10

Tengo un servicio web que se ejecuta en un clúster de servidores. Este servicio web realiza algunos procesos internos, y luego puede hacer una llamada a un servicio externo que incurre en una tarifa.Optimistic Caching Concurrency Design Patrón

Quiero poner algo de almacenamiento en caché para que si recibo solicitudes idénticas al servicio (lo cual está garantizado), entonces no tengo que repetir el procesamiento, ahorrando tiempo de procesamiento/potencia y también el costo incurrido en la parte externa de la llamada de servicio.

Sin embargo, estoy luchando para encontrar la manera de gestionar este almacenamiento en caché cuando tenga las siguientes limitaciones

  • el servicio se ejecuta en varios servidores web de alta disponibilidad y escalabilidad
  • La solicitud puede tomar hasta a 5 segundos para responder, pero mientras tanto, puede haber recibido 2 o 3 solicitudes idénticas.

¿Cómo puedo retrasar la ejecución de las otras llamadas de servicio, hasta que la primera ha respondido (por lo tanto, disponible en la memoria caché), cuando se trabaja en un entorno distribuido.

He pensado en poner un patrón de proxy frontal y crear una cola de solicitudes idénticas dentro del proxy, de modo que cuando la primera vuelva, también pueda devolver la misma respuesta a las demás. ¿Es este el patrón correcto, o hay un mejor patrón de concurrencia que se ocupa de este escenario?

+0

es el ws externo agrupado también, ¿tiene usted el control sobre él? parece que representa un buen objetivo para empezar, puede escribir su propio servicio web de caché contenedor frente a él –

+0

no está en mi control. Es un servicio al que llamamos desde un proveedor – Codemwnci

+0

Como en cada escenario _cache_: ¿Están realmente seguros de que el servicio es _realmente sin estado? Es decir. un servicio "FetchCustomerDetailById" no es cachable porque un intermedio "ChangeCustomerName" tendría que invalidar su caché. –

Respuesta

6

Se podría

  1. calcular un hash criptográfica de la solicitud
  2. ver si el resultado ya está en la base de datos de este hash, y si es así, devolverlo
  3. tienda el hash en la base de datos con un estado "resultado pendiente"
  4. llame al servicio web y actualice la fila en la base de datos con el resultado.

En el paso 2, si el hash ya está en la base de datos, con el estado "resultado pendiente", puede sondear la base de datos cada X milisegundos y finalmente devolver el resultado una vez que está allí.

El diablo está en los detalles, por supuesto, ya que tendría que decidir lo que hacer en caso de que se produzca un error:

  • Qué te devolverá un error para todas las solicitudes idénticas subsiguientes?
  • ¿hace que los hilos de espera vuelvan a intentar llamar al servicio web?
  • ¿devuelve un error, pero solo durante un tiempo, y luego vuelve a intentarlo?
+0

@downvoters: ¿te importa explicar tus downvotes? –

+0

También me gustaría entender los votos abajo. 2 votos al alza (uno mío), 2 abajo, pero sin explicación? ¿Hay algo malo acerca de tu solución en @fyr's? – Codemwnci

+0

IIUC, la solución de fyr es tener un caché por servidor y evitar tener una base de datos porque es un punto único de falla. Supuse que ya tenía una base de datos central, ya que el 99.9% de las aplicaciones tienen una. Así que diseñé una solución donde todos los servidores usan esta base de datos central como un caché persistente. –

2

1.) El servicio se ejecuta en varios servidores web de alta disponibilidad y escalabilidad

tratar esta simplemente como restricción de diseño. Esto significa que no incorpore el nombre de host en su método de búsqueda de caché. Siempre que el resultado no dependa del host, no deberías tener ningún problema. Considero que es un defecto de diseño si hostA devuelve algo diferente de hostB en un entorno HA con el mismo servicio y los mismos parámetros.

Si desea mantener el sistema redundante, no debe tener un caché central. Porque la mayoría de las veces una solución "central" es un sinónimo de "punto único de falla": solución. El bloqueo también es más complejo si se sincroniza en los servidores de aplicaciones.

Cuántas memorias caché que introduce dependen un poco de la tasa de aciertos de caché y de los recursos que tiene disponibles en sus sistemas. La solución más simple es almacenar en caché en un nivel de instancia por servicio.

2.) La solicitud puede tomar hasta 5 segundos para responder, pero en la media hora, I puede haber recibido 2 o 3 otras solicitudes idénticas.

Esto también es una restricción de diseño. Divides tu almacenamiento en caché simplemente en 2 pasos diferentes.

  1. Primera insertar una llave para su petición idéntica si el primer hilo entra en la rutina de almacenamiento en caché y el acceso de bloqueo a su valor
  2. insertar el valor después de procesar terminado y libre de la cerradura

Es necesario también para manejar el manejo de excepciones.

El mecanismo locking y connection podría implementarse con diferentes estrategias

  • síncrono - que acaba de hacer un mutex/semáforo o lo que sea y el acceso de bloqueo a la sección crítica. Que puede terminar teniendo algunas solicitudes en el estado de espera hasta que se cierre el candado
  • Asincrónico - implementa algún tipo de mecanismo de sondeo que dará como resultado un mensaje que dice que los datos no están listos si un hilo se encuentra con una sección crítica bloqueada (como en el procesamiento síncrono). Esto no dará lugar a muchas conexiones abiertas, pero introduce una mayor complejidad.

el mutex/semáforo o cualquier estructura que se utiliza para bloquear el acceso a la sección crítica podría depender de la clave única (siempre y cuando no se desea serializar el acceso a su servicio) que ha calculado para el idénticos solicitud.

+0

absolutamente, todos los hosts devolverán lo mismo. Solo están divididos en diferentes nodos para HA/escalabilidad. Pero la restricción que produce es que ya no estamos en un dominio serial, sino en un dominio de procesamiento paralelo. Por lo tanto, sugiere un caché central al que puedan acceder todos los nodos que empleen un mecanismo de bloqueo – Codemwnci

+0

, el proxy de almacenamiento en caché debe estar antes del equilibrador de carga; si dejas pasar una solicitud, podrías dejarla funcionar además de solo esperarla en un nodo –

+0

@Codemwnci Ajusté mi publicación a esta pregunta. – fyr