Primer implementacije sprotnega tekstovnega pogovora s knjižnico SignalR
Avtorja: Denis Balant, Enej Hudobreznik
Za prenos podatkov preko spletu se še danes primarno uporablja protokol HTTP (angl. Hypertext Transfer Protocol) oz. njegova sodobnejša in varnejša šifrirana izpeljanka HTTPS (angl. HTTP Secure). Osnovana sta na arhitekturi odjemalec-strežnik, kjer odjemalec (angl. client) pošlje strežniku (angl. Server) zahtevo (angl. HTTP request), strežnik pa nanj odgovori z odzivom (angl. HTTP response).
Protokola HTTP in HTTPS sta primerna za enostavne prenose tipa poizvedba-odgovor (npr. prenos HTML datoteke na zahtevo odjemalca). Uporabnik lahko z uporabo teh dveh protokolov pridobi posodobljene podatke šele, ko pošlje novo zahtevo, zato nista najprimernejša za prenos podatkov v realnem času (npr. spletni klepet, spletne videoigre, spremljanje lokacije … ), na čemer temelji veliko funkcionalnosti sodobnejših spletnih aplikacij. Sprotnost lahko implementiramo na veliko različnih in boljših načinov, ki imajo vsak svoje prednosti in slabosti.
Najenostavnejši način implementacije je tekoče izpraševanje (angl. regular polling), kjer odjemalec ponavljajoče vsak izpraševalni interval (angl. polling interval) pošilja strežniku zahtevke za nove podatke ne glede na to, ali se je vsebina sploh spremenila. Kljub svoji enostavnosti ta pristop ni primeren za obsežnejše storitve, kjer dostop do strežnika zahteva veliko odjemalcev, ker ga lahko velika količina nepotrebnih in ponovljenih zahtev preobremeni in tako upočasni delovanje storitve.
Izboljšavo dosežemo z uvedbo dolgotrajnega izpraševanja, kjer odjemalec strežniku na enak način pošilja zahtevke, a nanje strežnik ne odgovori, dokler ne zazna prisotnosti novih podatkov. Slabost takšnega pristopa je ta, da mora imeti odjemalec definiran določen časovni interval, po preteku katerega prepozna strežnikovo nedejavnost za napako. To doda nastavitvi odjemalca in strežnika dodatno plast kompleksnosti.
Sodobni standard HTML5 ponuja tudi API imenovan Server-Sent Events. Gre za protokol, kjer odjemalcu ni treba periodično pošiljati zahtev strežniku , ampak mu ta ob spremembah ponovno pošlje potrebne podatke in s tem olajša sprotno komunikacijo.
Standard HTML5 ponuja tudi protokol WebSockets, ki omogoča pravo obojestransko sprotno komunikacijo. Na začetku vzpostavitve se izvede rokovanje (angl. handshake), kjer se odjemalec in strežnik dogovorita o množici standardov, ki jih bosta uporabljala. Po uspešnem rokovanju se med udeležencema odpre trajna povezava z majhno zakasnitvijo. Ta protokol je še posebej uporaben, ko gre za povezavo tipa point-to-point (direktna povezava med odjemalcem in strežnikom).
Poleg klasične arhitekture odjemalec-strežnik obstajajo še druge rešitve, kjer povezava med odjemalci ne teče preko strežnikov, ampak neposredno med odjemalci, ki igrajo tako vlogo odjemalca kot strežnika (t. i. peer-to-peer omrežja). Popularen protokol za sprotno komunikacijo v takšnih omrežjih je WebRTC.
V okolju .NET za sprotno komunikacijo lahko uporabimo odprtokodno knjižnico SignalR, ki je del spletnega ogrodja ASP.NET Core, zato jo je preprosto dodati obstoječim projektom kot dodatno stopnjo vmesnega programja (angl. middleware) pri obdelavi dohodnih zahtev. Knjižnica simulira klice metod na odjemalcu iz strani strežnika in klice metod na strežniku iz strani odjemalca (angl. Remote Procedure Call – RPC), vendar ne zagotavlja ustreznosti št. parametrov in njihovih tipov. S strežnika lahko kličemo metode na vseh odjemalcih, na določeni skupini odjemalcev ali pa samo enem odjemalcu.
Njena glavna prednost je v preprostosti implementacije zahvaljujoč paketom za razvoj programske opreme (SDK) za mnogo različnih platform, ki programerjem skrijejo notranje delovanje storitve, in možnosti preprostega gostovanja s storitvijo Azure SignalR. Dodatna prednost je, da storitev sama izbere najustreznejšo vrsto povezave (opisane zgoraj) med odjemalcem in strežnikom.
Uporaba knjižnice temelji na posebnih razredih, imenovanih vozlišča (angl. hubs), ki predstavljajo abstrakcijo povezave med odjemalci in strežnikom. Zanje definiramo metode, ki jih lahko odjemalci kličejo na strežniku.
Spodnji primer prikazuje primer preproste implementacije tekstovnega pogovora. Najprej podamo definicijo osnovnega vozlišča za pogovor na strežniku. Metode tega razreda predstavljajo metode, ki jih lahko kličejo odjemalci na strežniku. Ob klicu metode SendMessage torej odjemalec pošlje id uporabnika (userId) in vsebino sporočila (message), strežnik pa na vseh odjemalcih kliče metodo ReceiveMessage s podanima parametroma.
Spodnji primer prikazuje implementacijo spletnega odjemalca v jeziku JavaScript z uradno knjižnico (@microsoft/signalr). Odjemalci se povežejo na URL-naslov vozlišča (v spodnjem primeru /chat). Povezavo predstavlja objekt connection. Metoda on predstavlja poslušalca na klic metode, ime katere podamo kot prvi parameter, kot drugega pa funkcijo, ki se ob dogodku izvede. Metode na strežniku izvedemo prek metode invoke objekta connection, ki kot prvi argument zahteva ime metode, naslednji pa so njeni parametri.
Privzeto se podatki med strežnikom in odjemalci prenašajo v formatu JSON, knjižnica pa podpira tudi učinkovitejši format MessagePack.
Čeprav knjižnica močno olajša razvoj, ima svoje pomanjkljivosti. Ena glavnih je skalabilnost sistema pri lastnem gostovanju in zagotavljanje neprekinjenega delovanja, saj je njeno izvajanje zelo težko porazporediti na več ločenih strežnikov. To se izkaže še posebej težavno, če želimo zmanjšati zakasnitev za oddaljene uporabnike in gostovati na več različnih lokacijah, saj je zasnova rešitve omejena samo na eno. Prav tako knjižnica ne zagotavlja zanesljive dostave in vrstnega reda sporočil, kar lahko poslabša uporabniško izkušnjo.