Il problema Le piattaforme web offrono un meccanismo semplice per offrire servizi a comunità di utenti ◦ Grazie al paradigma client/server che non richiede l'installazione di software specifico ed offre, in molti casi, prestazioni sufficienti per le esigenze di computazione Applicazioni web in tempo reale Vi sono casi in cui occorre ricevere aggiornamenti in tempo reale da parte del server Anno Accademico 2013-14 ◦ Lavoro condiviso, giochi, chat, aste on-line, cruscotti per il monitoraggio di informazioni remote, ... Il paradigma reuqest/response del protocollo HTTP è troppo limitativo ◦ Occorre introdurre meccanismi alternativi Applicazioni Internet Applicazioni Internet I possibili approcci Polling semplice Storicamente sono stati messi a punto meccanismi differenti, con vari limiti ◦ ◦ ◦ ◦ I client fanno richiesta di informazioni al server ◦ Questo risponde immediatamente, eventualmente indicando che non ci sono nuovi dati disponibili Polling Long polling (Comet) Streaming di contenuti WebSocket Approccio che non scala al crescere del numero degli utenti Tutti condividono il principio che la comunicazione deve essere iniziata dal lato client ◦ Rispettando il modello architetturale HTTP, con l'eventuale presenza di proxy/NAT/firewall intermedi ◦ Evitando di richiedere più di due connessioni verso lo stesso host contemporaneamente Applicazioni Internet 3 Long polling (1) Applicazioni Internet 4 Per poter gestire questo tipo di situazioni, lo strato applicativo sul server mantiene, per ogni possibile client, una coda di messaggi ◦ Se il server non è in grado di fornire le informazioni richieste (ad esempio perché non ci sono dati più recenti di quelli già noti al client) "parcheggia" la richiesta ◦ Quando ci sono informazioni disponibili, utilizza la richiesta pendente per inviarle ◦ Alla ricezione di una risposta, il client esegue immediatamente una nuova richiesta ◦ A seguito di eventi a livello applicativo, nuovi messaggi rivolti ad uno o più client vengono generati lato server ed inseriti nelle corrispettive code Quando un client effettua un data richiesta al server, questo esamina la corrispondente coda e, nel caso, inoltra tutti i messaggi ad esso destinati, chiudendo la risposta Se, mentre è in attesa di risposta,, il client deve inviare dati al server, apre una connessione parallela ◦ Il server può usare l'identificativo di sessione per correlare tale richiesta a quella pendente 5 Streaming di contenuti ◦ Se la coda è vuota, la comunicazione viene tenuta aperta, in sospeso, per un dato intervallo di tempo Se entro il lasso di tempo fissato, si genera un nuovo messaggio, esso viene inserito nella coda e immediatamente recapitato usando la connessione in sospeso ◦ Se il tempo scade, la connessione viene chiusa indicando un timeout Applicazioni Internet 6 Problematiche comuni al long polling ed allo streaming Modello simile al long polling, basato sulla codifica di trasferimento chuncked ◦ I client aprono una connessione verso il server che configura la risposta nella modalità Transfer-Encoding: chunked ◦ Tale risposta non viene mai terminata dal server, che si limita ad inviare, di volta in volta, nuovi chunk effettuando il flush delle comunicazione Via via che i dati vengono trasferiti al client questo può elaborarli I server web utilizzano, per lo più, un modello di concorrenza adatto a gestire molte richieste di breve elaborazione ◦ Allocando un thread estratto da un thread-pool ad ogni connessione ricevuta o a ciascuna richiesta in esse contenute ◦ Tale thread rimane impegnato fino a che l'intera risposta non è stata generata e trasmessa al richiedente L'utilizzo di richieste che durano a lungo nel tempo vincola il thread usato per gestire la risposta ad attendere la chiusura della stessa ◦ Non tutti i browser supportano correttamente tale meccanismo Applicazioni Internet ◦ Iniziare una nuova connessione ha un elevato costo in termini di rete (7 pacchetti IP solo per gestire il ciclo di vita) ◦ I dati utili sulla connessione sono una frazione minima (bassa efficienza) ◦ Produce un effetto simile ad un attacco DDOS Long polling (2) Meccanismo di richiesta con risposta potenzialmente ritardata Applicazioni Internet 2 ◦ Portando, rapidamente, all'esaurimento delle risorse disponibili 7 Applicazioni Internet 8 Servlet asincroni (1) Servlet asincroni (2) La specifica JavaEE 6 ha introdotto la possibilità di dichiarare un servlet in modalità asincrona Per funzionare in modalità asincrona, la classe del servlet deve essere annotata con l'attributo ◦ Se, all'arrivo di una richiesta, non ci sono le informazioni necessarie per generare la risposta, è possibile metterla in attesa, lasciando libero il thread che la sta servendo di fare altro ◦ asynchSupported=true All'atto della ricezione di una richiesta, se ne può sospendere la valutazione invocando il metodo La richiesta viene incapsulata in un oggetto di tipo AsyncContext ◦ Tale oggetto potrà essere utilizzato in seguito per generare la risposta ◦ AsyncContext ctx = request.startAsync(); ◦ L'oggetto "ctx" può essere archiviato in una struttura dati in attesa delle informazioni da inviare al client E' possibile fissare anche un tempo limite di elaborazione oltre il quale la connessione deve essere chiusa ◦ Registrando un ascoltatore sull'oggetto AsynContext Applicazioni Internet Applicazioni Internet 9 Servlet asincroni (3) 10 Implementare la logica lato server (1) Gestire richieste asincrone presuppone che il server disponga di un meccanismo per organizzare e distribuire i messaggi @WebServlet(urlPatterns="/test", asyncSupported=true) public class TestServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { ◦ Poiché le richieste possono arrivare da fonti diverse ed essere elaborate da thread differenti, occorre adottare politiche di sincronizzazione che evitino le interferenze senza penalizzare le prestazioni ◦ Occorre in particolare fare attenzione ad evitare lock globali che serializzano l'accesso da parte di tutti i client AsyncContext ac = req.startAsync(); ac.addListener(new AsyncListener() { public void onComplete(AsyncEvent event) throws IOException { event.getSuppliedResponse() .getOutputStream() .print("Complete"); } public void onError(AsyncEvent event) { } public void onStartAsync(AsyncEvent event) { } public void onTimeout(AsyncEvent event) { } }); queueContextForFurtherProcessing(ac); Poiché le comunicazioni coinvolgono spesso sessioni (ed utenti) diverse, occorre gestire tali strutture dati a livello di servletContext ◦ Iniettando gli opportuni oggetti tramite un ServletContextListener } } Applicazioni Internet Applicazioni Internet 11 Implementare la logica lato server (2) 12 Implementare la logica lato server (3) @WebServletContextListener public class SampleWebService implements ServletContextListener { public class TestServlet extends HttpServlet { //..altri metodi public void contextInitialized(ServletContextEvent sce) { ExecutorService executor = Executors.newFixedThreadPool(10); sce.getServletContext().setAttribute("service", executor); } private void queueContextForFurtherProcessing(final AsyncContext ac) { ExecutorService service (ExecutorService) getServletContext() .getAttribute("service"); service.submit(new Runnable() { public void run() { ServletRequest req= ac.getRequest(); public void contextDestroyed(ServletContextEvent sce) { ExecutorService executor = (ExecutorService) sce.getServletContext().getAttribute("service"); service.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (! service.awaitTermination(60, TimeUnit.SECONDS)) service.shutdownNow(); // Cancel currently executing tasks } catch (InterruptedException ie) { service.shutdownNow(); Thread.currentThread().interrupt(); } } //elabora la richiesta //al termine chiudi la richiesta ac.complete(); } }); } } } Applicazioni Internet 13 Diagramma temporale Client Executor Il long polling trasferisce, per ogni connessione, l'insieme completo di intestazioni per richiesta e risposta doPost() Async Context Network getRequest() Runnable submit() ◦ Dando origine ad un livello di efficienza molto limitato L'eventuale presenza di proxy (trasparenti o meno) può condizionare l'esito della transazione run() ◦ Spesso, infatti, i proxy introducono un limite massimo di attesa, oltre al quale chiudono la connessione dichiarandola fallita ◦ Per evitare questo, occorre anticipare il timeout lato server, penalizzando ulteriormente l'efficienza del servizio complete() Applicazioni Internet 14 I limiti di questo approccio TestServlet Container Applicazioni Internet 15 Applicazioni Internet 16 Web Socket (1) WebSocket (2) Protocollo di comunicazione bidirezioanle asincrono definito dal documento RFC 6455 (2011) L'uso di una connessione permanente ottimizza l'utilizzo delle risorse di rete ◦ Instaurato tra un client ed un server con una richiesta HTTP/Upgrade ◦ Offre ai client basati su browser un meccanismo alternativo al long polling ◦ Una volta stabilita tale connessione, non occorre trasferire intestazioni né indirizzare in modo esplicito la sessione La comunicazione è full-duplex ◦ I messaggi possono essere mandati in modo indipendente dal client al server e viceversa ◦ I messaggi possono contenere dati arbitrari, sia testuali che in formato binario Il protocollo è basato su una connessione TCP che viene tenuta aperta per tutta la durata della sessione di lavoro ◦ Presuppone, lato client, l'adozione di un modello a pagina singola, all'interno del quale è presento codice JavaScript che instaura/sovraintende tale connessione Applicazioni Internet È possibile instaurare una connessione crittografata basata sul protocollo TLS 17 WebSocket (3) Una connessione web socket attraversa due fasi ◦ Pressoché tutti i browser desktop supportano tale protocollo (InternetExplorer solo dalla versione 10 !!!) ◦ Disponibile su iOS a partire dalla versione 6 del sistema operativo (parzialmente supportato anche nelle versioni 5.x) ◦ Disponibile per il browser nativo Android solo dalla versione 4.4 (in parte supportato da Chrome, Firefox e OperaMobile nelle precedenti versioni) Supporto lato server ◦ Standard per le implementazioni JavaEE7 (JSR356) ◦ Implementazioni proprietarie per Tomcat /Glassfish /... ◦ Moltissimi altri framework basati su JavaScript (Socket.IO), Ruby (FireHose.IO), Perl (Mojolicio.us,Web::Hippie), ... 19 L'interfaccia applicativa lato client ◦ Occorre dapprima verificarne il supporto da parte del browser ◦ Si crea un istanza dell'oggetto WebSocket ◦ Si inizia la connessione al server ◦ Si registrano le callback per gli eventi ◦ Si inviano/ricevono i messaggi ◦ Si chiude la connessione 21 Metodi e proprietà dell'oggetto WebSocket La negoziazione inizia con una richiesta del client di tipo GET alla URL del servizio ◦ Indicando l'intestazione "Upgrade: websocket", la versione del protocollo richiesto (13), una chiave di sessione (Sec-WebSocket-Key) ◦ Il server risponde con lo status-code 101 (Switching protocols) ed include un'intestazione di tipo Sec-WebSocket-Accept il cui valore è derivato algoritmicamente dalla chiave inviata nella richiesta Lo scambio di dati consente l'invio di contenuti testuali (utf-8), binari e di controllo ◦ Questi ultimi servono per la gestione della chiusura della connessione I dati scambiati possono avere dimensioni arbitrarie ◦ Il protocollo internamente frammenta e ricombina tali dati per facilitare l'opera degli eventuali host intermedi e ridurre le dimensioni dei buffer Applicazioni Internet 20 //verifica la compatibilità del browser if ( window.WebSocket ) { // creo la connessione verso l'host var socket = new WebSocket("ws://echo.websocket.org"); socket.onopen = function (event) { //azioni da eseguire all'atto della connessione } socket.onmessage = function (event) { var data=event.data; //azioni da eseguire alla ricezione di un messaggio } socket.onclose = function(event) { //azioni da eseguire alla chiusura del socket } socket.onerror = function (event) { //azioni da eseguire in caso di errore } if (socket.readyState===WebSocket.OPEN) socket.send("Hello,WebSocket!"); } else { alert("WebSocket non supportati"); } Applicazioni Internet 22 La componente server in Java In base al contenitore adottato, possono essere richieste implementazioni differenti send(data) ◦ Invia il parametro al server ◦ Questo metodo deve essere invocato solo se lo stato della connessione è OPEN ◦ Le versioni più recenti di Tomcat, Wildfly, Jetty e GlassFish supportano la specifica JSR 356 ◦ In contenitori più vecchi, è possibile fare affidamento su framework specifici, come Atmosphere (https://github.com/Atmosphere), che aggregano altre forme di trasporto close(code, reason) ◦ Chiude la connessione, eventualmente indicando al server un valore numerico (code) e la ragione della terminazione (stringa) url – indica la URL del server protocol – indica il protocollo adottato dal server readyState – Indica lo stato della connessione E' possibile creare implementazioni non basate su container ◦ OPEN, CLOSED, CONNETTING e CLOSING bufferedAmount – indica il numero di byte non ancora inviati binaryType – indica il formato dei dati ricevuti Applicazioni Internet ◦ La negoziazione iniziale (handshake) ◦ Lo scambio di dati Accesso lato client Si instaura una connessione WebSocket programmaticamente, via JavaScript, secondo i seguenti passi: Applicazioni Internet 18 Struttura del protocollo Supporto lato client Applicazioni Internet Applicazioni Internet 23 ◦ Netty (http://netty.io) ◦ Play! Framework (www.playframework.com) ◦ Vertx (http://vertx.io) Applicazioni Internet 24 La specifica JSR356 Inviare e ricevere messaggi (1) Prevede che le sessioni di lavoro di un websocket siano gestite da istanze di POJO la cui classe è preceduta dall'annotazione La classe Session modella il canale di comunicazione ◦ Un istanza di tale oggetto è sempre passata come parametro ai metodi che ne riportano gli eventi legati al ciclo di vita ◦ @ServerEndpoint(value="/relative/url") ◦ Dove "/relative/url" costituisce la URL relativa al server corrente presso cui il websocket è contattabile Offre i metodi getBasicRemote() e getAsyncRemote(), mediante i quali è possibile interagire con il client remoto I metodi di tale classe annotati con @OnOpen, @OnClose, @OnMessage, @OnError ◦ ◦ ◦ ◦ void open(Session s, EndpointConfig cfg) void close(Session s) void message(Session s, String msg) void error(Session s, Throwable t) Applicazioni Internet ◦ Il primo offre un meccanismo di comunicazione bloccante, il secondo offre metodi che restituiscono oggetti di tipo Future che si avverano nel momento in cui il messaggio è stato trasmesso 25 Inviare e ricevere messaggi (2) Applicazioni Internet 26 Gestire lo stato di una connessione La ricezione dei messaggi è regolata dalle annotazioni poste sui metodi della classe che implementa il web socket Ogni volta che si instaura una nuova connessione, il contenitore crea una nuova istanza della classe annotata come endpoint ◦ Ci possono essere al massimo tre metodi con l'annotazione @OnMessage, distinti in base ai parametri ricevuti ◦ Si possono utilizzare gli attributi di tale classe per memorizzare informazioni legate allo stato della connessione @ServerEndpoint("/mutlipleReceivers") public class Receiver { L'oggetto Session offre anche una mappa (String->Object), accessibile tramite il metodo getUserProperties(), in cui si possono salvare ulteriori informazioni @OnMessage public void textMessage(Session session, String msg) {} @OnMessage public void binaryMessage(Session session, ByteBuffer msg) {} @OnMessage public void pongMessage(Session session, PongMessage msg) {} } Applicazioni Internet 27 Comunicare con tutti i client connessi 28 Interagire con il server http Sebbene il ciclo di vita di un web socket sia sostanzialmente indipendente da quello della pagina che ne ospita la componente client, è possibile creare un punto di contatto nel momento in cui si instaura la comunicazione Gli oggetti di classe Session offrono anche accesso a tutte le sessioni attualmente in corso ◦ Si può sfruttare questa informazione per gestire comunicazioni di gruppo, come chat e aste on line @ServerEndpoint("/echoall") public class EchoAllEndpoint { @OnMessage public void onMessage(Session session, String msg) { try { for (Session sess : session.getOpenSessions()) { if (sess.isOpen()) sess.getBasicRemote().sendText(msg); } } catch (IOException e) { ... } } } Applicazioni Internet Applicazioni Internet ◦ Il parametro di tipo EndpointConfig permette di accedere all'interfaccia HandshakeRequest attraverso la quale si può richiedere l'oggetto HttpSession o le intestazioni ed i parametri della richiesta di apertura @OnOpen public void onOpen(Session s, EndpointConfig cfg) { HandshakeRequest req=(HandshakeRequest)cfg.getUserProperties() .get("handshakereq"); HttpSession session=(HttpSession)req.getHttpSession(); } 29 Applicazioni Internet 30
© Copyright 2024 Paperzz