Novinky
Vývoj8 min čtení

Real-time funkce ve webové aplikaci: notifikace a živá data

Kdy má smysl stavět real-time webovou aplikaci a kdy stačí jen častější načítání dat. Přehled možností — polling, server-sent events a WebSockety — a hlavně to, co každá z nich stojí při provozu, škálování a testování.

Požadavek přichází do zadání téměř vždy ve stejné podobě: „chceme, aby se to aktualizovalo živě, bez obnovování stránky“. Zní to jako detail — jde přece jen o to, aby číslo na obrazovce nezaostávalo za realitou. Ve skutečnosti je to jedno z rozhodnutí, která nejvíc mění architekturu aplikace, náklady na provoz i to, co všechno musí tým po spuštění sledovat.

Rozdíl je principiální. Běžná webová aplikace odpovídá na otázky: přijde požadavek, vrátí se odpověď, spojení se zavře a server na klienta zapomene. Real-time webová aplikace si musí s každým otevřeným prohlížečem udržovat vztah — vědět, kdo je připojen, co ho zajímá, co mu už poslala a co poslat nestihla. Tento článek je o tom, jak k takovému požadavku přistoupit střízlivě: kdy dává smysl, jaké možnosti existují, co každá z nich stojí a jaké pasti čekají ve chvíli, kdy spojení spadne nebo dva lidé upraví stejný záznam.

Potřebujete opravdu real-time, nebo jen čerstvější data?

První otázka není technická, ale produktová: co špatného se stane, když uživatel uvidí údaj starý patnáct sekund? U velké části firemních aplikací je odpověď „nic“. Vedoucí, který se dívá na přehled, nepotřebuje, aby se čísla měnila přímo před očima — potřebuje, aby nebyla včerejší. Dispečer přidělující vozidla nebo dva lidé ve stejném formuláři jsou jiný případ; tam zpoždění přímo způsobuje kolize a duplicitní práci.

Pomáhá rozdělit obrazovky do tří skupin:

  • Skutečný real-time (do sekundy). Rozhodnutí se dělá okamžitě a několik lidí pracuje se stejným objektem: chat, souběžná editace, dispečink, monitoring s alarmy.
  • Čerstvá data (jednotky až desítky sekund). Seznamy objednávek, stavy úkolů, provozní přehledy. Uživatel toleruje krátké zpoždění, jen nechce klikat na obnovit.
  • Dávkové zobrazení. Reporty, uzávěrky, přehledy za období. Tady je „živost“ spíš na škodu — čísla, která se mění během čtení, se špatně interpretují a ještě hůř porovnávají.

Druhá skupina je v praxi nejpočetnější a téměř vždy jí stačí periodické načítání. Klient se každých pár sekund zeptá serveru, jestli je něco nového; s podmíněnými požadavky (ETag, If-Modified-Since) je odpověď většinou prázdná a levná. Když je záložka na pozadí, interval se prodlouží nebo zastaví. Nevzniká žádná nová infrastruktura, projde to přes každou firemní proxy, chyby se ladí běžnými nástroji a opakování požadavku je zabudované v samotném principu. Pokud je cílem hlavně to, aby vedení vidělo aktuální čísla bez ručního exportu, téma rozebíráme podrobněji v textu o reportech pro vedení firmy v reálném čase.

Zkratka: Dokud neumíte pojmenovat konkrétní rozhodnutí, které se pokazí při zpoždění deseti sekund, stavte polling a ušetřené peníze dejte do něčeho, co uživatel opravdu pocítí.

Polling, SSE a WebSockety: co každá volba stojí

Pokud jste prošli předchozím filtrem a real-time skutečně potřebujete, výběr přenosové vrstvy je hlavně rozhodnutí o provozu, ne o rychlosti. Všechny varianty umí doručit data dost rychle; liší se tím, co po vás pak chtějí každý den.

PřístupTok datTypická latenceProvozní náročnostKdy je dobrá volba
Periodický pollingklient se ptáinterval (např. 5–30 s)nejnižšípřehledy, seznamy, stavy úkolů
Long pollingklient se ptá, server držítéměř okamžitěstředníkdyž potřebujete rychlost, ale ne obousměrnost
Server-sent events (SSE)server → klienttéměř okamžitěstřednínotifikace, živé grafy, streamované odpovědi
WebSocketyobousměrněnejnižšínejvyššíchat, souběžná editace, hry, dispečink
Hostovaná real-time službapodle poskytovateletéměř okamžitěnízká vývojově, vyšší finančněmalý tým, který nechce provozovat vlastní vrstvu

Server-sent events jsou v projektech, které vídáme, nejčastěji podceňovanou volbou. Jde o obyčejné HTTP spojení, které server nechá otevřené a posílá do něj řádky textu. Automatické obnovení spojení i číslování zpráv jsou součástí standardu, funguje to přes běžnou HTTP infrastrukturu a na straně serveru nepřibývá samostatný protokol. Pokud potřebujete jen směr „ze serveru k uživateli“ — a u notifikací a živých grafů je to přesně ten případ — WebSockety jsou často zbytečně těžké řešení.

WebSockety zvolte tehdy, když klient reálně posílá hodně zpráv zpět a záleží na pořadí a latenci obou směrů. Cenou je vlastní protokol nad spojením, vlastní autentizace a autorizace pro každou zprávu a nutnost řešit to, co HTTP řeší za vás. Rozdíl mezi technologiemi je nakonec menší než rozdíl mezi „máme to zapnuté“ a „umíme to provozovat“, což je téma, kterému se věnujeme i při návrhu webových aplikací na míru.

Každé otevřené spojení je malý kus stavu, který musíte někde držet, platit za něj a jednou ho i korektně zavřít.

Stav, který se rozpadne: proč real-time UI potřebuje rekonciliaci

Na tomhle bodě padne většina prvních implementací. Vývojář otestuje funkci na kancelářské wi-fi, vidí, že zprávy chodí, a považuje věc za hotovou. Realita je ale jiná: mobilní síť se přepne, notebook si zdřímne, firemní proxy zabije nečinné spojení po minutě, nasazení nové verze odpojí naráz všechny. Uživatel přitom nedostane žádné varování — obrazovka jednoduše přestane žít a tváří se, že je aktuální.

Proto platí jednoduché pravidlo: tok zpráv není zdroj pravdy, je to jen optimalizace. Zdrojem pravdy zůstává server a rozhraní musí mít cestu, jak se s ním kdykoli srovnat. Prakticky to znamená tři věci:

  • Detekce. Pravidelný heartbeat oběma směry a viditelný indikátor stavu spojení. Když uživatel vidí „obnovujeme spojení“, neudělá rozhodnutí nad mrtvými daty.
  • Obnova. Po opětovném připojení buď dotáhněte chybějící události od posledního známého pořadového čísla, nebo — což je jednodušší a pro většinu aplikací zcela dostačující — načtěte celý stav obrazovky znovu a stream použijte až od té chvíle.
  • Idempotence. Počítejte s tím, že zpráva přijde dvakrát nebo v jiném pořadí. Zpracování události musí být bezpečné opakovat, jinak se v seznamu objeví duplicity, které nikdo neumí vysvětlit.

Optimistické změny a konflikty při souběžné editaci

Optimistické UI — tedy zobrazení změny okamžitě, ještě před potvrzením serveru — je to, co dává aplikaci pocit svižnosti. Zároveň je to nejčastější zdroj tichých nekonzistencí. Každá optimistická změna potřebuje tři stavy: čekající, potvrzená, zamítnutá. Zamítnutí musí umět změnu vrátit zpět a srozumitelně to oznámit, ne ji jen tiše přepsat.

Při souběžné editaci se rozhodujete mezi třemi režimy. Nejjednodušší je zámek na záznamu — dokud s ním někdo pracuje, ostatní vidí jen pro čtení; je to nepohodlné, ale předvídatelné. Druhou možností je verzování na úrovni záznamu: klient posílá verzi, kterou viděl, a server odmítne zápis nad novější verzí. Třetí, nejdražší možnost je slučování na úrovni polí nebo datové struktury navržené pro automatické slučování, které potřebujete prakticky jen u společného psaní textu.

Pozor: Model „vyhrává poslední zápis“ bez verzování nezpůsobí chybu, kterou byste viděli v logu — způsobí tiše ztracené změny, které se objeví až jako stížnost uživatele o týden později.

Škálování real-time vrstvy

Dokud běží jeden aplikační server, je všechno jednoduché: událost vznikne a server ji pošle svým připojeným klientům. Ve chvíli, kdy přidáte druhý server, to přestane fungovat — uživatel připojený na server A se nedozví o události, která vznikla na serveru B. Řešením je distribuční vrstva, přes kterou si servery události navzájem rozesílají: message broker nebo publish/subscribe kanál.

S tím souvisejí další tři věci, které je třeba vyřešit dřív než v produkci, ne potom:

  • Přilepené relace a časové limity. Load balancer musí vědět, že spojení má zůstat na stejném uzlu, a jeho limit nečinnosti musí být delší než interval heartbeatu.
  • Limity počtu spojení. Jedno spojení spotřebuje paměť a systémový deskriptor. Kapacita uzlu se tak neplánuje podle počtu požadavků za sekundu, ale podle počtu současně přihlášených lidí.
  • Nápor po nasazení. Restart odpojí všechny naráz a všichni se naráz pokusí připojit zpět. Bez exponenciálního odstupu s náhodným rozptylem si tím aplikace složí vlastní start.

Nezapomeňte ani na autorizaci. U klasického HTTP se oprávnění ověřují při každém požadavku; u dlouho otevřeného spojení se ověří jednou při připojení a potom může člověk o přístup přijít, ale data mu chodí dál. Kontrola musí být při odeslání každé zprávy. Širšímu kontextu růstu zátěže se věnujeme v článku o škálování webové aplikace s růstem firmy.

Design notifikací: kanál, dávkování a pozornost

Technická část notifikací je jednodušší než ta produktová. Rozhodnutí, které rozhoduje o úspěchu, zní: která událost si zaslouží vyrušit člověka a která má jen tiše počkat v aplikaci.

KanálVhodný proHlavní riziko
In-app seznamběžné události, kontext práceuživatel ho nemusí otevřít
Push do prohlížečečasově citlivé událostipovolení se ptá jednou, odmítnutí je trvalé
E-mailsouhrny, věci na pozdějisnadno skončí v zapomenuté složce
SMS a voláníkritická selhání, pohotovostdrahé a rušivé; jen pro úzkou skupinu

Pár zásad, které se osvědčily bez ohledu na obor: nikdy neupozorňujte člověka na jeho vlastní akci; slučujte související události do jedné zprávy s odstupem několika minut místo pěti samostatných; dejte nastavení podle typu události, ne jen jeden vypínač na všechno; a synchronizujte stav „přečteno“ mezi zařízeními, jinak uživatel řeší totéž dvakrát. Pokud uvažujete o push notifikacích bez nativní aplikace, jde prakticky o téma progresivních webových aplikací — a je třeba počítat s tím, že podpora se mezi platformami stále liší.

Provozní daň: co real-time přidá k monitoringu a testování

Real-time funkce přinášejí třídu chyb, kterou klasický monitoring nezachytí. Server odpovídá, chybovost je nula, stránka se načte rychle — a přesto polovina uživatelů vidí stará data, protože jim před hodinou spadlo spojení a nikdy se neobnovilo. Proto potřebujete měřit jiné věci: počet aktivních spojení, míru opětovných připojení, zpoždění mezi vznikem události a jejím doručením, počet nedoručených zpráv a hloubku fronty v brokeru.

Testování je podobná kapitola. Automatizované testy musí umět otevřít dvě relace naráz a ověřit, že akce v jedné se projeví ve druhé; musí umět spojení uměle přerušit a zkontrolovat, že se stav dorovnal. Zátěžový test musí simulovat tisíc současně otevřených spojení, ne tisíc požadavků za sekundu — to jsou zcela odlišné scénáře. Jak takové scénáře zapadají do celkového procesu kontroly kvality, popisujeme v textu o QA procesu před spuštěním softwaru.

Počítejte také s tím, že real-time vrstva je zdrojem nákladů, které nezmizí po předání projektu. Otevřená spojení se platí časem, ne požadavky, takže měsíční účet roste s počtem přihlášených lidí i tehdy, když nikdo nic nedělá.

Shrnutí

Real-time webová aplikace není funkce, kterou „doděláte na konci“ — je to rozhodnutí o architektuře, provozu a rozpočtu na další roky. Postup, který se osvědčil, je nudný, ale funguje: nejdřív pojmenujte konkrétní rozhodnutí, které zpoždění pokazí; pokud takové není, udělejte polling a hotovo. Pokud je, vyberte nejjednodušší vrstvu, která stačí — u směru ze serveru k uživateli jsou to obvykle server-sent events. Potom, ještě před prvním řádkem UI, navrhněte, jak se stav dorovná po výpadku, a teprve nakonec řešte optimistické změny a konflikty.

Notifikace navrhujte jako produkt, ne jako technický vedlejší produkt: méně zpráv ve správném kanálu má vždy větší hodnotu než všechno okamžitě všude. A počítejte s tím, že monitoring a testování budou stát víc než u běžné aplikace — je to daň, kterou platíte za to, že obrazovka žije.

Pokud zvažujete, jestli má ve vaší aplikaci real-time smysl a v jakém rozsahu, rádi to s vámi projdeme na konkrétních obrazovkách — ozvěte se nám a začneme tím, co se opravdu musí dít okamžitě.

INTERFASE