design-patterns - patterns - rest architecture
Largas interacciones RESTful (7)
proporcionar un feed de Atom para cada recurso, de modo que el cliente se suscriba para actualizar eventos en lugar de simplemente OBTENER el recurso (parece demasiado complicado y posiblemente tenga hambre de recursos si hay muchos documentos activos)
¿Has considerado SUP ?
Si el sondeo es una opción, ¿por qué molestarse con un feed? ¿Por qué no simplemente hacer que los clientes sondeen el recurso en sí?
¿Podría reducir las encuestas innecesarias al incluir un tiempo estimado para completar los análisis?
Tenemos una discusión en curso en mi equipo en este momento, y me interesarían otros puntos de vista. Supongamos que tenemos un servicio web RESTful cuya función es anotar documentos mediante la aplicación de una variedad de algoritmos y servicios de análisis. La interacción básica en claro: tenemos un recurso que es la colección de documentos; el cliente envía un nuevo documento a la colección, recupera el URI del nuevo documento, luego puede OBTENER ese docURI
para recuperar el documento o OBTENER {docURI}/metadata
para ver los metadatos generales, {docURI}/ne
para las entidades nombradas , etc. El problema es que algunos de los análisis pueden tardar mucho tiempo en completarse. Supongamos que el cliente OBTIENE el URI de metadatos antes de que se complete el análisis, porque quiere poder mostrar resultados parciales o incrementales en la UI. Repetir el GET en el futuro puede arrojar más resultados.
Las soluciones que hemos discutido incluyen:
- manteniendo la conexión HTTP abierta hasta que se realicen todos los análisis (lo que no parece escalable)
- usar encabezados de
content-length
yaccept-range
para obtener contenido incremental (pero no sabemos de antemano cuánto tiempo será el contenido final) - proporcionar un feed de Atom para cada recurso, de modo que el cliente se suscriba para actualizar eventos en lugar de simplemente OBTENER el recurso (parece demasiado complicado y posiblemente tenga hambre de recursos si hay muchos documentos activos)
- simplemente haciendo que GET devuelva lo que está disponible en ese momento (pero aún deja el problema de que el cliente sepa cuándo finalmente terminamos) [editado para eliminar la referencia a idempotencia después de los comentarios] .
¿Alguna opinión o sugerencia para formas alternativas de manejar interacciones de larga duración o asincrónicas en una arquitectura RESTful?
Ian
- simplemente ignorando la idempotencia y teniendo GET devolver lo que esté disponible en ese momento (pero aún deja el problema de que el cliente sepa cuándo finalmente terminamos).
¿Un GET que arroja resultados diferentes a lo largo del tiempo significa realmente que no es idempotente? La especificación dice:
Los métodos también pueden tener la propiedad de "idempotencia" en el sentido de que (aparte de los problemas de error o caducidad) los efectos colaterales de N> 0 solicitudes idénticas son los mismos que para una sola solicitud
Es decir, las llamadas múltiples a GET pueden arrojar resultados diferentes, siempre y cuando las llamadas mismas sean libres de efectos secundarios.
En ese caso, tal vez su método REST podría usar los mecanismos condicional GET y caché para indicar cuándo está listo:
- Mientras el análisis está en progreso, una respuesta
GET {docURI}/metadata
podría tener:- un encabezado
Expires
establecido en unos pocos segundos en el futuro. - sin encabezado
ETag
.
- un encabezado
- Una vez que se realiza el análisis, las respuestas para ese recurso tienen:
- sin encabezado
Expires
. - un
ETag
. Las solicitudes posteriores conETag
deben devolver304 Not Modified
.
- sin encabezado
Nota: es posible que desee considerar los otros encabezados de respuesta involucrados en el almacenamiento en caché, no solo Caduca.
Esto "se siente" como un diseño RESTful. Puede imaginarse que un navegador web está haciendo lo correcto, ya que realizó solicitudes consecutivas a este recurso.
"solo tener GET devolver lo que esté disponible en ese momento" tiene mucho sentido. Excepto, cuando están encuestando, no quieres seguir devolviendo cosas que ya saben. Las respuestas se vuelven más largas cada vez que sondean.
Necesitas que te proporcionen su "lo que he visto hasta ahora" en la solicitud GET. Esto te da idempotencia. Si piden el trozo 1, siempre obtienen la misma respuesta. Una vez que han visto el trozo 1, pueden pedir el trozo 2.
La respuesta no se hace más grande. Más piezas disponibles. Un GET "nivel de colección" proporciona el tamaño de la respuesta. Tiene GET "nivel de detalle" para cada pieza disponible.
Básicamente, este es un algoritmo como el reconocimiento de TCP / IP. Cuando aceptan una pieza, envían la siguiente pieza. Si hay una pieza siguiente, de lo contrario enviará un 200-nada nuevo para informar.
El "problema de que el cliente sepa cuando finalmente hemos terminado" es imponderable. No pueden saber y no puedes predecir cuánto tiempo tomará.
Usted no quiere que estén "ocupados esperando", sondeando para ver si ya terminó, eso es una gran carga para su servidor. Si son impacientes. Puede estrangular sus solicitudes. Puede enviarles un "control de regreso en x segundos" donde x se hace progresivamente más grande.
Incluso puede utilizar un algoritmo de planificador de estilo Unix donde su puntaje disminuye cuando sondean y suben si no sondean durante X segundos.
La alternativa es algún tipo de cola en la que les devuelva los resultados. Para hacer esto, tendrían que proporcionar un URI que pudieras enviar por correo para decirles que terminaste.
O bien, usan Atom para una arquitectura de encuesta ligera. Si bien Atom parece complejo, y aún implica encuestas, usted brinda una respuesta mínima de Atom ("aún no cambiada") hasta que termina, cuando proporciona ("nuevos resultados") para que pueda obtener el verdadero peso pesado. Esto es para todo o nada, en lugar de la técnica de respuesta incremental anterior.
También puede pensar en el GET "nivel de colección" como su estado Atom en el proceso como un todo.
Lo implementaría de la siguiente manera:
1) el cliente solicita metadatos
2) el servidor devuelve datos reales (si ya están disponibles) o NotReady
3) el cliente pregunta al servidor cuándo estarán disponibles los datos (este paso puede ser una fusión con el anterior)
4) el servidor devuelve el intervalo de tiempo (puede haber alguna heurística para el número total de trabajos en ejecución, etc.)
5) el cliente espera durante un período de tiempo específico e ir al paso 1
De esta forma, puede proporcionar datos a los clientes lo antes posible. Puede dar forma a la carga del servidor ajustando el intervalo de retraso devuelto en el paso 4)
Use HTTP 202 aceptado .
Además, echa un vistazo a los servicios web RESTful , es donde aprendí sobre lo anterior.
Una solución alternativa que puede o no ser adecuada en su caso es agregar un nuevo punto final llamado "AnnotationRequests". Publique el documento (o un enlace) en el punto final AnotaciónRequest y debe devolver una Ubicación (por ejemplo, http://example.org/AnnotationRequest/2042 ) que le permitirá a su cliente sondear el estado del proceso. Cuando se completa el proceso, la representación "AnnotationRequest" puede contener un enlace al documento completo.
Un buen efecto colateral de esto es que puede hacer un GET en AnotaciónRequests para ver los documentos que están siendo procesados ​​actualmente. Depende de usted decidir durante cuánto tiempo desea mantener las peticiones de anotación. Puede ser valioso mantener un historial completo de cuándo fueron solicitados, quién y cuánto tiempo tomó cada uno, o podría desecharlos periódicamente.
Puede consultar el nServiceBus de Udi Dahan .