Come architettura un'applicazione web basata su webs in tempo reale e pesante?

15

Nel processo di sviluppo di un'applicazione per pagina singola in tempo reale, ho progressivamente adottato websocket per potenziare i miei utenti con dati aggiornati. Durante questa fase, mi ha fatto triste notare che stavo distruggendo troppo la struttura della mia app e non sono riuscito a trovare una soluzione a questo fenomeno.

Prima di entrare nello specifico, solo un po 'di contesto:

  • La webapp è una SPA in tempo reale;
  • Il backend è in Ruby on Rails. Gli eventi in tempo reale vengono trasferiti da Ruby a una chiave Redis, quindi un server di micro-nodi lo ritira e lo invia a Socket.Io;
  • Il frontend è in AngularJS e si connette direttamente al server socket.io nel nodo.

Sul lato server, prima del realtime avevo una chiara separazione basata su controller / modello delle risorse, con l'elaborazione associata a ciascuna. Questo design classico MVC è stato completamente distrutto, o almeno scavalcato, proprio quando ho iniziato a inviare materiale tramite websocket ai miei utenti. Ora ho un single pipe in cui tutta la mia app scorre più o meno dati strutturati . E lo trovo stressante.

Sul front end, la preoccupazione principale è la duplicazione della logica aziendale. Quando l'utente carica la pagina, devo caricare i miei modelli attraverso le chiamate AJAX classiche. Ma devo anche gestire l'inondazione di dati in tempo reale e mi ritrovo a duplicare gran parte della logica di business lato client per mantenere la coerenza dei miei modelli lato client.

Dopo alcune ricerche, non riesco a trovare buoni post, articoli, libri o qualsiasi altra cosa che possa dare consigli su come si possa e si debba progettare l'architettura di una moderna webapp con alcuni argomenti specifici in mente:

  • Come strutturare i dati inviati dal server all'utente?
    • Devo inviare solo eventi come "questa risorsa è stata aggiornata e dovresti ricaricarla tramite una chiamata AJAX" o spingere i dati aggiornati e sostituire i dati precedenti caricati tramite chiamate AJAX iniziali?
    • Come definire uno scheletro coerente e scalabile per i dati inviati? questo è un messaggio di aggiornamento del modello o "c'era un errore con blahblahblah" messaggio
  • Come non inviare dati su tutto, da qualsiasi postazione nel back-end?
  • Come ridurre la duplicazione della logica di business sia sul server che sul lato client?
posta Philippe Durix 05.08.2016 - 11:17
fonte

2 risposte

9

How to structure the data that is sent from the server to the user?

Utilizza il schema di messaggistica . Bene, stai già utilizzando un protocollo di messaggistica, ma intendo strutturare le modifiche come messaggi ... in particolare eventi. Quando cambia il lato server, ciò si traduce in eventi aziendali. Nel tuo scenario, le tue opinioni cliente sono interessate a questi eventi. Gli eventi dovrebbero contenere tutti i dati rilevanti per quel cambiamento (non necessariamente tutti i dati della vista). La pagina del client dovrebbe quindi aggiornare le parti di vista che mantiene con i dati dell'evento.

Ad esempio, se stavi aggiornando un ticker azionario e AAPL cambiato, non dovresti spingere tutti i prezzi delle azioni verso il basso o anche tutti i dati su AAPL (nome, descrizione, ecc.). Dovresti solo spingere AAPL, il delta e il nuovo prezzo. Sul client, si aggiornerà quindi solo il prezzo delle azioni sulla vista.

Should I only send events like "this resource has been updated and you should reload it via an AJAX call" or push the updated data and replace previous data loaded via initial AJAX calls?

Direi nessuno dei due. Se stai inviando l'evento, vai avanti e invia i relativi dati (non i dati dell'intero oggetto). Dagli un nome per il tipo di evento che è. (La denominazione e i dati rilevanti per quell'evento vanno oltre l'ambito dei meccanismi meccanici del sistema. Questo ha più a che fare con il modo in cui la logica aziendale è modellata.) I tuoi updaters di vista devono sapere come tradurre ogni evento specifico in un cambio di visualizzazione preciso (cioè aggiorna solo ciò che è cambiato).

How to define a coherent and scalable skeleton to data sent? is this a model update message or "there was an error with blahblahblah" message

Direi che questa è una grande domanda a risposta aperta che dovrebbe essere suddivisa in diverse altre domande e pubblicata separatamente.

In generale, tuttavia, il tuo sistema di back-end dovrebbe creare e inviare eventi per eventi importanti alla tua attività. Questi potrebbero venire da feed esterni o da attività nel back-end stesso.

How not to send data about everything from anywhere in the backend?

Utilizza il modello di pubblicazione / sottoscrizione . Quando la tua SPA carica una nuova pagina che è interessata a ricevere aggiornamenti in tempo reale, la pagina dovrebbe iscriversi solo a quegli eventi che può utilizzare, e chiamare la logica di aggiornamento della visualizzazione al suo ingresso. Probabilmente avrai bisogno di logica pub / sub il server per ridurre il carico di rete. Esistono librerie per Websocket pub / sub, ma non sono sicuro di cosa siano nell'ecosistema Rails.

How to reduce the business logic duplication both on the server and the client side?

Sembra che tu debba aggiornare i dati della vista sia sul client che sul server. La mia ipotesi è che hai bisogno dei dati della vista lato server in modo da avere uno snapshot per avviare il client in tempo reale. Essendo che ci sono due lingue / piattaforme coinvolte (Ruby e Javascript), la logica di aggiornamento della vista dovrà essere scritta in entrambi. A parte il transpiling (che ha i suoi problemi), non vedo un modo per aggirare questo.

Punto tecnico: la manipolazione dei dati (aggiornamento della vista) non è una logica aziendale. Se intendi utilizzare la convalida dei casi, ciò sembra inevitabile poiché le convalide del cliente sono necessarie per una buona esperienza utente, ma non possono essere considerate attendibili dal server.

Ecco come vedo strutturata una cosa del genere.

Visualizzazioni client:

  • Richiede un'istantanea della vista e l'ultimo numero di evento visto della vista
    • Questo prepopolare la vista in modo che il client non debba creare da zero.
    • Potrebbe essere su HTTP GET per semplicità
  • Crea una connessione websocket e si iscrive a eventi specifici, a partire dall'ultimo numero di evento della vista.
  • Riceve gli eventi su websocket e aggiorna la visualizzazione in base al tipo di evento / dati.

Comandi client:

  • Richiesta di modifica dei dati (HTTP PUT / POST / DELETE)
    • La risposta è solo riuscita o non riuscita + errore
    • (Gli eventi generati dalla modifica arriveranno su websocket e attivano un aggiornamento della vista.)

Il lato server potrebbe essere suddiviso in più componenti con responsabilità limitate. Uno che elabora solo le richieste in arrivo e crea eventi. Un altro potrebbe gestire le sottoscrizioni dei clienti, ascoltare gli eventi (ad esempio in-process) e inoltrare gli eventi appropriati agli abbonati. Potresti avere un terzo che ascolta gli eventi e aggiorna le visualizzazioni sul lato server - forse questo accade anche prima che gli abbonati ricevano gli eventi.

Quello che ho descritto è una forma di CQRS + Messaggi e una strategia tipica per affrontare il tipo di problemi che stai affrontando.

Non ho portato Event Sourcing in questa descrizione perché non sono sicuro che sia qualcosa che vuoi prendere su o se ne hai bisogno necessariamente. Ma è un modello correlato.

    
risposta data 05.08.2016 - 20:07
fonte
4

Dopo alcuni mesi di lavoro sul back-end, sono stato in grado di utilizzare alcuni dei consigli qui presenti per risolvere i problemi che la piattaforma stava affrontando.

L'obiettivo principale del ripensamento del back-end era di attaccare il più duro possibile al CRUD. Tutte le azioni, i messaggi e le richieste sparsi su molti percorsi sono stati raggruppati in risorse create, aggiornate, lette o eliminate . Sembra ovvio ora, ma questo è stato un modo molto difficile di pensare per applicare con attenzione.

Dopo che tutto è stato organizzato in risorse, sono stato in grado di allegare messaggi in tempo reale ai modelli.

  • La creazione attiva un messaggio con nuova risorsa foro;
  • Aggiorna attiva un messaggio con solo gli attributi aggiornati (più l'UUID);
  • La cancellazione attiva un messaggio di eliminazione.

Sull'API Rest, tutti i metodi di creazione, aggiornamento, cancellazione generano una sola risposta di testa, il codice HTTP che informa del successo o dell'errore e i dati effettivi che vengono trasmessi tramite websocket.

Nel front-end, ogni risorsa viene gestita da un componente specifico che le carica tramite HTTP all'inizializzazione, quindi sottoscrive gli aggiornamenti e mantiene il loro stato nel tempo. Le viste quindi si collegano a questi componenti per visualizzare le risorse ed eseguire azioni su quelle risorse attraverso gli stessi componenti.

Ho trovato che il CQRS + Messaging e Event Sourcing sono molto interessanti, ma ho ritenuto che fosse un po 'complicato per il mio problema ed è forse più adatto alle applicazioni intensive dove trasferire dati in un database centralizzato è un lusso costoso. Ma terremo sicuramente presente questo approccio.

In questo caso, l'app avrà pochi client simultanei e ho preso la parte di affidarsi molto al database. I modelli più mutevoli sono archiviati in Redis, di cui mi fido per gestire alcune centinaia di aggiornamenti al secondo.

    
risposta data 18.01.2017 - 12:10
fonte

Leggi altre domande sui tag