ventajas una tutorial sirve qué para integrar español ejemplo diseño arquitectura rest design websocket jax-rs restful-architecture

una - rest api tutorial español



¿Cómo combinar websockets y http para crear una API REST que mantenga los datos actualizados? (10)

Con toda la información excelente, todas las personas geniales añadidas antes que yo.

Descubrí que, finalmente, no existe el bien o el mal, simplemente se reduce a lo que se adapta a sus necesidades:

vamos a tomar CRUD en este escenario:

Solo enfoque WS:

Create/Read/Update/Deleted information goes all through the websocket. --> e.g If you have critical performance considerations ,that is not acceptable that the web client will do successive REST request to fetch information,or if you know that you want the whole data to be seen in the client no matter what was the event , so just send the CRUD events AND DATA inside the websocket.

WS PARA ENVIAR INFORMACIÓN DEL EVENTO + RESTO PARA CONSUMIR LOS DATOS EN SÍ MISMO

Create/Read/Update/Deleted , Event information is sent in the Websocket, giving the web client information that is necessary to send the proper REST request to fetch exactly the thing the CRUD that happend in server.

por ejemplo, WS envía UsersListChangedEvent {"ListChangedTrigger:" ItemModified "," IdOfItem ":" XXXX # 3232 "," UserExtrainformation ":" Suficiente información para que el cliente decida si es relevante para que obtenga los datos modificados "}

Descubrí que usar WS [Solo para usar datos de eventos] y REST [Para consumir los datos] es mejor porque:

[1] Separación entre el modelo de lectura y escritura. Imagine que desea agregar algo de información de tiempo de ejecución cuando se recuperan sus datos cuando se lee desde REST, que ahora se logra porque no está mezclando los modelos de escritura y lectura como en 1.

[2] Digamos que otra plataforma, no necesariamente el cliente web consumirá esta información. por lo tanto, solo debe cambiar el desencadenador de evento de WS a la nueva forma y usar REST para consumir los datos.

[3] El cliente no necesita escribir 2 formas de leer los datos nuevos / modificados. generalmente también hay un código que lee los datos cuando se carga la página, y no
a través del websocket, este código ahora se puede usar dos veces, una vez cuando se carga la página, y el segundo cuando WS activó el evento específico.

[4] Tal vez el cliente no quiere buscar al nuevo usuario porque muestra actualmente solo una vista de datos antiguos [por ejemplo, los usuarios], y los nuevos cambios de datos no le interesan.

Estoy pensando en construir una API REST con websockets y http, donde utilizo websockets para decirle al cliente que hay nuevos datos disponibles o proporcionar los nuevos datos directamente al cliente.

Aquí hay algunas ideas diferentes de cómo podría funcionar:
ws = websocket

Idea A:

  1. David obtiene todos los usuarios con GET /users
  2. Jacob agrega un usuario con POST /users
  3. Se envía un mensaje ws a todos los clientes con información de que existe un nuevo usuario
  4. David recibe un mensaje por ws y llamadas GET /users

Idea B:

  1. David obtiene todos los usuarios con GET /users
  2. David se registra para recibir actualizaciones de ws cuando se realiza un cambio a /users
  3. Jacob agrega un usuario con POST /users
  4. El nuevo usuario es enviado a David por ws

Idea C:

  1. David obtiene todos los usuarios con GET /users
  2. David se registra para recibir actualizaciones de ws cuando se realiza un cambio a /users
  3. Jacob agrega un usuario con POST /users y obtiene el id 4
  4. David recibe el ID 4 del nuevo usuario por ws
  5. David obtiene el nuevo usuario con GET /users/4

Morí:

  1. David obtiene todos los usuarios con GET /users
  2. David se registra para recibir actualizaciones de ws cuando se realizan cambios a /users .
  3. Jacob agrega un usuario con POST /users
  4. David recibe un mensaje de ws de que se hacen cambios a /users
  5. David obtiene solo el delta llamando a GET /users?lastcall=''time of step one''

¿Qué alternativa es la mejor y cuáles son los pros y los contras?
¿Es otra mejor ''Idea E''?
¿Necesitamos usar REST o es suficiente para todos los datos?

Editar
Para resolver problemas con la desincronización de los datos, podemos proporcionar el encabezado
"If-Unmodified-Since"
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Unmodified-Since
o "E-Tag"
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
o ambos con solicitudes PUT.



En general, puede echar un vistazo a los marcos web actuales "en tiempo real" como MeteorJS que abordan exactamente este problema.

Meteor en trabajos específicos más o menos como su ejemplo D con suscripciones en ciertos datos y deltas que se envían después de cambios solo a los clientes afectados. Su protocolo utilizado se llama DDP que además envía los deltas no como HTML propenso a la sobrecarga, sino como datos brutos.

Si los websockets no están disponibles, se pueden utilizar los retrocesos, como sondeos prolongados o eventos enviados por el servidor .

Si planea implementarlo usted mismo, espero que estas fuentes sean una especie de inspiración sobre cómo se ha abordado este problema. Como ya se dijo, el caso de uso específico es importante


La Idea B es para mí lo mejor, porque el cliente se suscribe específicamente a los cambios en un recurso y obtiene las actualizaciones incrementales a partir de ese momento.

¿Necesitamos usar REST o es suficiente para todos los datos?

Por favor, compruebe: WebSocket / REST: ¿Conexiones de clientes?


La respuesta depende de tu caso de uso. En su mayor parte, he descubierto que puedes implementar todo lo que necesites con sockets. Siempre y cuando solo intente acceder a su servidor con clientes que puedan admitir sockets. Además, la escala puede ser un problema cuando solo se usan sockets. Aquí hay algunos ejemplos de cómo puede usar solo sockets.

Lado del servidor:

socket.on(''getUsers'', () => { // Get users from db or data model (save as user_list). socket.emit(''users'', user_list ); }) socket.on(''createUser'', (user_info) => { // Create user in db or data model (save created user as user_data). io.sockets.emit(''newUser'', user_data); })

Lado del cliente:

socket.on(''newUser'', () => { // Get users from db or data model (save as user_list). socket.emit(''getUsers''); }) socket.on(''users'', (users) => { // Do something with users })

Esto usa socket.io para el nodo. No estoy seguro de cuál es su situación exacta, pero esto funcionaría para ese caso. Si necesita incluir puntos finales REST, también estaría bien.


No conozco Java, pero trabajé tanto con Ruby y C en estos diseños ...

Bastante gracioso, creo que la solución más fácil es usar JSON, donde la API REST simplemente agrega los datos del method (es decir, el method: "POST" ) al JSON y reenvía la solicitud al mismo controlador que usa Websocket.

La respuesta de la API subyacente (la respuesta de la API al manejar las solicitudes JSON) se puede traducir a cualquier formato que necesite, como la representación HTML ... aunque consideraría simplemente devolver JSON para la mayoría de los casos de uso.

Esto ayuda a encapsular el código y mantenerlo SECO mientras se accede a la misma API usando REST y Websockets.

Como puede inferir, este diseño facilita las pruebas, ya que la API subyacente que maneja el JSON puede probarse localmente sin la necesidad de emular un servidor.

¡Buena suerte!

PS (Pub / Sub)

En cuanto a Pub / Sub, me parece que es mejor tener un "gancho" para las llamadas de API de actualización (una devolución de llamada) y un módulo de Pub / Sub separado que maneja estas cosas.

También me resulta más fácil editar los datos para el servicio Pub / Sub (opción B) en lugar de solo un número de referencia (opción C) o un mensaje de "actualización disponible" (opciones A y D).

En general, también creo que el envío de toda la lista de usuarios no es efectivo para sistemas más grandes. A menos que tengas de 10 a 15 usuarios, la llamada a la base de datos puede ser un fracaso. Considere la posibilidad de que el administrador de Amazon solicite una lista de todos los usuarios ... Brrr ....

En cambio, consideraría dividir esto en páginas, digamos 10-50 usuarios por página. Estas tablas se pueden llenar usando múltiples solicitudes (Websocket / REST, no importa) y se actualizan fácilmente usando mensajes de Pub / Sub en vivo o se vuelven a cargar si se pierde y se restablece una conexión.

EDITAR (RESTO vs. Websockets)

En cuanto a REST vs. Websockets ... me parece que la cuestión de la necesidad es principalmente un subconjunto de la pregunta "¿quién es el cliente?" ...

Sin embargo, una vez que la lógica se separa de la capa de transporte, el soporte de ambos es muy fácil y, a menudo, tiene más sentido para ambos.

Debo señalar que Websockets suele tener una ligera ventaja en lo que respecta a la autenticación (las credenciales se intercambian una vez por conexión en lugar de una por solicitud). No sé si esto es una preocupación.

Por el mismo motivo (y otros), los Websockets generalmente tienen una ventaja en cuanto a rendimiento ... cuán grande depende de la capa de transporte REST (HTTP / 1.1, HTTP / 2, etc.) una ventaja sobre REST.

Por lo general, estas cosas son insignificantes cuando llega el momento de ofrecer un punto de acceso API pública y creo que implementar ambas es probablemente la mejor opción.


Otra opción es usar Firebase Cloud Messaging :

Con FCM, puede notificar a una aplicación cliente que hay nuevos correos electrónicos u otros datos disponibles para sincronizar.

¿Como funciona?

Una implementación de FCM incluye dos componentes principales para enviar y recibir:

  • Un entorno confiable, como Cloud Functions for Firebase o un servidor de aplicaciones en el que crear, orientar y enviar mensajes.
  • Una aplicación de cliente iOS, Android o Web (JavaScript) que recibe mensajes.

El cliente registra su clave de Firebase en un servidor. Cuando hay actualizaciones disponibles, el servidor envía notificaciones automáticas a la clave de Firebase asociada con el cliente. El cliente puede recibir datos en la estructura de notificación o sincronizarlos con un servidor después de recibir una notificación.


Personalmente, he usado Idea B en producción y estoy muy satisfecho con los resultados. Utilizamos http://www.axonframework.org/ , por lo que cada cambio o creación de una entidad se publica como un evento a lo largo de la aplicación. Estos eventos se utilizan para actualizar varios modelos de lectura, que son básicamente tablas simples de Mysql que respaldan una o más consultas. Agregué algunos interceptores a los procesadores de eventos que actualizan estos modelos de lectura para que publiquen los eventos que acaban de procesar después de que los datos se hayan enviado a la base de datos.

La publicación de eventos se realiza a través de STOMP en sockets web. Se simplifica mucho si utiliza Spring Socket Web ( https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html framework-reference / html / https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html ). Así es como lo escribí:

@Override protected void dispatch(Object serializedEvent, String topic, Class eventClass) { Map<String, Object> headers = new HashMap<>(); headers.put("eventType", eventClass.getName()); messagingTemplate.convertAndSend("/topic" + topic, serializedEvent, headers); }

Escribí un pequeño configurador que usa la API de Spring Bean Factory para que pueda anotar mis manejadores de eventos Axon de esta manera:

@PublishToTopics({ @PublishToTopic(value = "/salary-table/{agreementId}/{salaryTableId}", eventClass = SalaryTableChanged.class), @PublishToTopic( value = "/salary-table-replacement/{agreementId}/{activatedTable}/{deactivatedTable}", eventClass = ActiveSalaryTableReplaced.class ) })

Por supuesto, esa es solo una forma de hacerlo. Conectarse del lado del cliente puede verse más o menos así:

var connectedClient = $.Deferred(); function initialize() { var basePath = ApplicationContext.cataDirectBaseUrl().replace(/^https/, ''wss''); var accessToken = ApplicationContext.accessToken(); var socket = new WebSocket(basePath + ''/wss/query-events?access_token='' + accessToken); var stompClient = Stomp.over(socket); stompClient.connect({}, function () { connectedClient.resolve(stompClient); }); } this.subscribe = function (topic, callBack) { connectedClient.then(function (stompClient) { stompClient.subscribe(''/topic'' + topic, function (frame) { callBack(frame.headers.eventType, JSON.parse(frame.body)); }); }); }; initialize();


prefiero la A , permite al cliente la flexibilidad de actualizar o no los datos existentes.

también con este método, la implementación y el control de acceso se vuelven mucho más fáciles.

por ejemplo, simplemente puede transmitir el evento userUpdated a todos los usuarios, esto ahorra tener una lista de clientes para difusiones específicas y los Access Controls and Authentications aplicadas para su ruta REST no tendrán que cambiar para volverse a aplicar porque el cliente va a hacer una solicitud GET de nuevo.

Muchas cosas dependen del tipo de aplicación que estés haciendo.


Para resumir tus ideas:

A: envíe un mensaje a todos los clientes cuando un usuario edite datos en el servidor. Todos los usuarios luego solicitan una actualización de todos los datos.
-Este sistema puede hacer muchas llamadas innecesarias al servidor en nombre de clientes que no están usando los datos. No recomiendo producir todo ese tráfico adicional ya que el procesamiento y envío de esas actualizaciones puede ser costoso.

B: Después de que un usuario extrae datos del servidor, se suscriben a las actualizaciones del servidor, que les envía información sobre lo que ha cambiado.
- Esto ahorra mucho tráfico de servidor, pero si alguna vez se desincroniza, publicará datos incorrectos a los usuarios.

C: a los usuarios que se suscriben a las actualizaciones de datos se les envía información sobre qué datos se han actualizado y luego los recuperan ellos mismos.
-Este es el peor de A y B en cuanto a que tendrás viajes adicionales entre tus usuarios y servidores solo para notificarles que necesitan hacer una solicitud de información que puede estar fuera de sincronización.

D: los usuarios que se suscriben a las actualizaciones reciben una notificación cuando se realizan cambios y luego solicitan el último cambio realizado en el servidor.
-Este presenta todos los problemas con C, pero incluye la posibilidad de que, una vez fuera de sincronización, puedas enviar datos que no tendrán sentido para tus usuarios, lo que podría colgar la aplicación del lado del cliente por todo lo que sabemos.

Creo que esta opción E sería la mejor:
Cada vez que los datos cambian en el servidor, envíe el contenido de todos los datos a los clientes que se han suscrito. Esto limita el tráfico entre sus usuarios y el servidor al mismo tiempo que les da la menor posibilidad de tener datos desfasados. Es posible que obtengan datos obsoletos si su conexión se interrumpe, pero al menos no les estarías enviando algo así como Delete entry 4 cuando no estás seguro de si recibieron o no el mensaje de que la entrada 5 acaba de pasar al espacio 4.

Algunas consideraciones:

  • ¿Con qué frecuencia se actualizan los datos?
  • ¿Cuántos usuarios necesitan actualizarse cada vez que ocurre una actualización?
  • ¿Cuáles son sus costos de transmisión? Si tiene usuarios en dispositivos móviles con conexiones lentas, eso afectará la frecuencia y la cantidad que puede permitirse enviarles.
  • ¿Cuántos datos se actualizan en una actualización determinada?
  • ¿Qué sucede si un usuario ve datos obsoletos?
  • ¿Qué sucede si un usuario obtiene datos fuera de sincronización?

El peor de los casos sería el siguiente: muchos usuarios, con conexiones lentas, que actualizan frecuentemente grandes cantidades de datos que nunca deberían estar obsoletos y, si no se sincronizan, se vuelven engañosos.