Speed up Your Web Applications with HTML5 WebSockets Yakov Fain, Farata Systems, USA
The Plan - HTTP request-response - Demo - Server-Sent Events - Demo - WebSocket - Demo
What Do You See?
HTTP Hacks •  Polling •  Long Polling •  Streaming
Polling
Long Polling Comet implementation: hanging GET or pending POST
Streaming
Getting Price Quotes from Google Finance
HTTP Request Response Demo
Introducing the Demo The Software The Server: GlassFish 4 (promoted build B88), Oracle The server-side Java app can generate random price quotes The Client: HTML5 Ext JS framework, Sencha The Browser: Chrome , Google Charles Proxy The Goal 1. The server generates random stock prices for a 12-stock portfolio 2. The user sends a request for price quote for a stock 3. The HTML client displays the received price 4. Observe what went over the network
The Server: Rest    @Path("stock")      public  class  RestResource  {              @GET              @Produces(value  =  MediaType.APPLICATION_JSON)              @Path("/{ticker}")              public  String  getRandomValue(@PathParam(value  =  "ticker")   String  ticker)  {                            return  new   Gson().toJson(RandomStocksGenerator.getDataForTicker(ticker));                        }      }      
The Client: AJAX in Ext JS        'mypanel  button[action=doRestCall]':                    click:  this.onRestCall       onRestCall:  function(btn)  {      var  ticker  =  Ext.ComponentQuery.query('mypanel  textfield[name=ticker]') [0].getValue();      var  rest_url  =  "http://"  +  document.location.host  +   document.location.pathname  +  "rest/stock/";      rest_url  =  rest_url  +  ticker;        Ext.Ajax.request({              url:  rest_url,              scope:  this,              success:  function(response)  {                      var  a  =  Ext.JSON.decode(response.responseText);                      a.price  =  parseFloat(a.price).toFixed(4);                      console.log(a);              }      });   }
Server-Sent Events
What’s SSE •  It’s not a request-response mode •  The browser subscribes to events from server by creating EventSource pointing to this server •  The server can send data to client at any time •  The browser receives an event + data
Browser Subscribes to SSE To listen to any messages:  source.onmessage  =  function(e)  {….};   var  source  =  new  EventSource('http://localhost:8080/stock/events');     source.addEventListener('open',  function(e)  {      //  Connection  was  opened.   },  false);     source.addEventListener('priceChanged',  function(e)  {      var  priceQuote  =  JSON.parse(e.data);      //…   },  false);     source.addEventListener('error',  function(e)  {      if  (e.readyState  ==  EventSource.CLOSED)  {          //  Connection  was  closed.      }   },  false);  
Pushing SSE from Server The server sends events as text messages with MIME text/event-­‐stream     Each message starts with data: and end with a pair /n/n: 'data:  {"price":  "123.45"}/n/n'   The browser concatenates all these messages separating them with /n.
Pushing from Glassfish (Jersey) @Path("stock")   public  class  SseResource  {      private  static  final  SseBroadcaster  BROADCASTER  =              new  SseBroadcaster();        private  boolean  isRunning  =  false;      private  Timer  broadcastTimer;        @GET      @Path("stock-­‐generator")      @Produces(SseFeature.SERVER_SENT_EVENTS)      public  EventOutput  itemEvents()  {          final  EventOutput  eventOutput  =  new  EventOutput();          BROADCASTER.add(eventOutput);          if  (!isRunning)  startBroadcastTask();          return  eventOutput;      }   }
Broadcasting in Jersey protected  void  startBroadcastTask()  {    broadcastTimer  =  new  Timer();    broadcastTimer    .schedule(new  SseBroadcastTask(BROADCASTER,  0),  0,  300);    this.isRunning  =  true;   } public  class  SseBroadcastTask  extends  TimerTask  {   private  final  SseBroadcaster  owner;     public  SseBroadcastTask(SseBroadcaster  owner,  int  timeout)  {      this.owner  =  owner;   }     @Override   public  void  run()  {      OutboundEvent  event  =  new  OutboundEvent.Builder().data(            String.class,   RandomStocksGenerator.getRandomValues().toJson()).build();      owner.broadcast(event);  
Push With Serer-Sent Events Demo
Introducing the SSE Demo The Software The Server: GlassFish 4 (promoted build B88), Oracle The Java app can generate random price quotes The Client: HTML5 Ext JS framework, Sencha The Chrome Browser, Google The Goal 1. The server generates random stock prices for a 12-stock portfolio 2. The server pushes 3 price quotes per second using SSE 3. The HTML client shows the prices in a data grid
WebSocket
WebSocket •  Standard W3C protocol (RFC6455) •  Java EE 7 includes WebSocket API (JSR-356) •  Web Browsers include window.WebSocket object. No plugins required.
How WebSocket Works Establish a socket connection via HTTP for the initial handshake. Switch the protocol from HTTP to a socket-based protocol. Send messages in both directions simultaneously. This is not a request-response model!
Protocol Upgrade: HTTP à WebSocket •  Client sends Upgrade HTTP-request •  Server confirms Upgrade •  Client receives Upgrade response from server •  Client changes WebSocket.readyState to open
HTTP://… HTTPS://… WS://… WSS://… URI Schemes The wss encryption is done the same way as in https
W3C: WebSocket Interface [Constructor(DOMString  url,  optional  (DOMString  or  DOMString[])  protocols)]   interface  WebSocket  :  EventTarget  {      readonly  attribute  DOMString  url;        const  unsigned  short  CONNECTING  =  0;                          const  unsigned  short  OPEN  =  1;      const  unsigned  short  CLOSING  =  2;      const  unsigned  short  CLOSED  =  3;      readonly  attribute  unsigned  short  readyState;      readonly  attribute  unsigned  long  bufferedAmount;        //  networking      [TreatNonCallableAsNull]  attribute  Function?  onopen;                  [TreatNonCallableAsNull]  attribute  Function?  onerror;      [TreatNonCallableAsNull]  attribute  Function?  onclose;      readonly  attribute  DOMString  extensions;      readonly  attribute  DOMString  protocol;                                              void  close([Clamp]  optional  unsigned  short  code,  optional  DOMString  reason);        //  messaging      [TreatNonCallableAsNull]  attribute  Function?  onmessage;                        attribute  DOMString  binaryType;      void  send(DOMString  data);                              void  send(ArrayBufferView  data);      void  send(Blob  data);   };
@ServerEndpoint in Java EE Client Your Java Endpoint and other classes POJO Decoder Encoder
WebSocket Endpoint  @ServerEndpoint(value  =  "/stock-­‐generator",                  encoders  =  {  StockMessageEncoder.class  },                  decoders  =  {  StockMessageDecoder.class  })   public  class  StocksEndpoint  {            @OnOpen            public  void  onOpen(Session  session)  {                  …            }            @OnMessage          public  void  onMessage(StockMessage  message,  Session  client)  {            }              @OnClose            public  void  onClose(Session  session)  {…                  …              }          …   }
public  class  StockMessageDecoder  implements   Decoder.Text<StockMessage>  {          @Override        public  StockMessage  decode(String  s)  throws  DecodeException  {                Gson  gson  =  new  Gson();                return  gson.fromJson(s,  StockMessage.class);        }   } public  class  StockMessageEncoder  implements   Encoder.Text<StockMessage>  {                  @Override          public  String  encode(StockMessage  object)  throws   EncodeException  {                  return  new  Gson().toJson(object);          }   } Decoder Encoder
Heartbeats: Pings and Pongs Heartbeats is a mechanism to check that connection is still alive. If a WebSocket implementation receives a ping it has to respond with a pong ASAP. There is no JavaScript API to support Pings and Pongs.
Web Socket Demo
The Software The Server: GlassFish 4 (promoted build B88), Oracle The Java app can generate random price quotes The Client: HTML5 Ext JS framework, Sencha The Chrome Browser, Google Charles proxy Introducing the WebSocket Demo The Goal 1. The server generates random stock prices for a 12-stock portfolio 2. The server pushes 20 price quotes per second using WebSocket 3. The HTML client shows the prices in a data grid
What was Pushed via WebSocket The size of each data push: The length price quote data: from 42 to 45 bytes WebSocket added overhead: 2 bytes {"symbol":  "APPL",  "id":  555,  "price":  "451.29"}  
Comparing Overheads Http Request-Response Each roundtrip has: request header: 429 bytes + 268 bytes response header + 46 bytes data Server-Sent Events Initial request header 406 bytes + 268 bytes response header Each push: 46 bytes data + 8 bytes message wrapper WebSocket Initial upgrade request: 406 bytes + 268 bytes Each push 46 bytes data + 2 bytes message wrapper It’s full-duplex, not HTTP-based, works much faster.
Caniuse.com: Browser Support
Reducing kilobytes of data to 2 bytes... and reducing latency from 150ms to 50 ms is far more than marginal. In fact, these two factors alone are enough to make WebSocket seriously interesting to Google. Ian Hickson, Google The lead HTML5 writer
Where to use WebSockets - Live trading/sports ticker - Controlling medical equipment over the web - Chat applications - Multiplayer online games - Real-time updating social streams
Unedited drafts of the book Enterprise Web Development are published at enterprisewebbook.com
Contact Info Farata Systems: faratasystems.com Personal blog: yakovfain.com Twitter: @yfain Podcasts in Russian: americhka.us Thank you!

Speed up your Web applications with HTML5 WebSockets

  • 1.
    Speed up YourWeb Applications with HTML5 WebSockets Yakov Fain, Farata Systems, USA
  • 3.
    The Plan - HTTPrequest-response - Demo - Server-Sent Events - Demo - WebSocket - Demo
  • 4.
  • 5.
    HTTP Hacks •  Polling • Long Polling •  Streaming
  • 6.
  • 7.
    Long Polling Comet implementation:hanging GET or pending POST
  • 8.
  • 9.
    Getting Price Quotesfrom Google Finance
  • 10.
  • 11.
    Introducing the Demo TheSoftware The Server: GlassFish 4 (promoted build B88), Oracle The server-side Java app can generate random price quotes The Client: HTML5 Ext JS framework, Sencha The Browser: Chrome , Google Charles Proxy The Goal 1. The server generates random stock prices for a 12-stock portfolio 2. The user sends a request for price quote for a stock 3. The HTML client displays the received price 4. Observe what went over the network
  • 12.
    The Server: Rest    @Path("stock")      public  class  RestResource  {              @GET              @Produces(value  =  MediaType.APPLICATION_JSON)              @Path("/{ticker}")              public  String  getRandomValue(@PathParam(value  =  "ticker")   String  ticker)  {                            return  new   Gson().toJson(RandomStocksGenerator.getDataForTicker(ticker));                        }      }      
  • 13.
    The Client: AJAXin Ext JS        'mypanel  button[action=doRestCall]':                    click:  this.onRestCall       onRestCall:  function(btn)  {      var  ticker  =  Ext.ComponentQuery.query('mypanel  textfield[name=ticker]') [0].getValue();      var  rest_url  =  "http://"  +  document.location.host  +   document.location.pathname  +  "rest/stock/";      rest_url  =  rest_url  +  ticker;        Ext.Ajax.request({              url:  rest_url,              scope:  this,              success:  function(response)  {                      var  a  =  Ext.JSON.decode(response.responseText);                      a.price  =  parseFloat(a.price).toFixed(4);                      console.log(a);              }      });   }
  • 14.
  • 15.
    What’s SSE •  It’snot a request-response mode •  The browser subscribes to events from server by creating EventSource pointing to this server •  The server can send data to client at any time •  The browser receives an event + data
  • 16.
    Browser Subscribes toSSE To listen to any messages:  source.onmessage  =  function(e)  {….};   var  source  =  new  EventSource('http://localhost:8080/stock/events');     source.addEventListener('open',  function(e)  {      //  Connection  was  opened.   },  false);     source.addEventListener('priceChanged',  function(e)  {      var  priceQuote  =  JSON.parse(e.data);      //…   },  false);     source.addEventListener('error',  function(e)  {      if  (e.readyState  ==  EventSource.CLOSED)  {          //  Connection  was  closed.      }   },  false);  
  • 17.
    Pushing SSE fromServer The server sends events as text messages with MIME text/event-­‐stream     Each message starts with data: and end with a pair /n/n: 'data:  {"price":  "123.45"}/n/n'   The browser concatenates all these messages separating them with /n.
  • 18.
    Pushing from Glassfish(Jersey) @Path("stock")   public  class  SseResource  {      private  static  final  SseBroadcaster  BROADCASTER  =              new  SseBroadcaster();        private  boolean  isRunning  =  false;      private  Timer  broadcastTimer;        @GET      @Path("stock-­‐generator")      @Produces(SseFeature.SERVER_SENT_EVENTS)      public  EventOutput  itemEvents()  {          final  EventOutput  eventOutput  =  new  EventOutput();          BROADCASTER.add(eventOutput);          if  (!isRunning)  startBroadcastTask();          return  eventOutput;      }   }
  • 19.
    Broadcasting in Jersey protected  void  startBroadcastTask()  {    broadcastTimer  =  new  Timer();    broadcastTimer    .schedule(new  SseBroadcastTask(BROADCASTER,  0),  0,  300);    this.isRunning  =  true;   } public  class  SseBroadcastTask  extends  TimerTask  {   private  final  SseBroadcaster  owner;     public  SseBroadcastTask(SseBroadcaster  owner,  int  timeout)  {      this.owner  =  owner;   }     @Override   public  void  run()  {      OutboundEvent  event  =  new  OutboundEvent.Builder().data(            String.class,   RandomStocksGenerator.getRandomValues().toJson()).build();      owner.broadcast(event);  
  • 20.
  • 21.
    Introducing the SSEDemo The Software The Server: GlassFish 4 (promoted build B88), Oracle The Java app can generate random price quotes The Client: HTML5 Ext JS framework, Sencha The Chrome Browser, Google The Goal 1. The server generates random stock prices for a 12-stock portfolio 2. The server pushes 3 price quotes per second using SSE 3. The HTML client shows the prices in a data grid
  • 22.
  • 23.
    WebSocket •  Standard W3Cprotocol (RFC6455) •  Java EE 7 includes WebSocket API (JSR-356) •  Web Browsers include window.WebSocket object. No plugins required.
  • 24.
    How WebSocket Works Establisha socket connection via HTTP for the initial handshake. Switch the protocol from HTTP to a socket-based protocol. Send messages in both directions simultaneously. This is not a request-response model!
  • 25.
    Protocol Upgrade: HTTPà WebSocket •  Client sends Upgrade HTTP-request •  Server confirms Upgrade •  Client receives Upgrade response from server •  Client changes WebSocket.readyState to open
  • 26.
    HTTP://… HTTPS://… WS://… WSS://… URISchemes The wss encryption is done the same way as in https
  • 27.
    W3C: WebSocket Interface [Constructor(DOMString  url,  optional  (DOMString  or  DOMString[])  protocols)]   interface  WebSocket  :  EventTarget  {      readonly  attribute  DOMString  url;        const  unsigned  short  CONNECTING  =  0;                          const  unsigned  short  OPEN  =  1;      const  unsigned  short  CLOSING  =  2;      const  unsigned  short  CLOSED  =  3;      readonly  attribute  unsigned  short  readyState;      readonly  attribute  unsigned  long  bufferedAmount;        //  networking      [TreatNonCallableAsNull]  attribute  Function?  onopen;                  [TreatNonCallableAsNull]  attribute  Function?  onerror;      [TreatNonCallableAsNull]  attribute  Function?  onclose;      readonly  attribute  DOMString  extensions;      readonly  attribute  DOMString  protocol;                                              void  close([Clamp]  optional  unsigned  short  code,  optional  DOMString  reason);        //  messaging      [TreatNonCallableAsNull]  attribute  Function?  onmessage;                        attribute  DOMString  binaryType;      void  send(DOMString  data);                              void  send(ArrayBufferView  data);      void  send(Blob  data);   };
  • 28.
    @ServerEndpoint in JavaEE Client Your Java Endpoint and other classes POJO Decoder Encoder
  • 29.
    WebSocket Endpoint  @ServerEndpoint(value  =  "/stock-­‐generator",                  encoders  =  {  StockMessageEncoder.class  },                  decoders  =  {  StockMessageDecoder.class  })   public  class  StocksEndpoint  {            @OnOpen            public  void  onOpen(Session  session)  {                  …            }            @OnMessage          public  void  onMessage(StockMessage  message,  Session  client)  {            }              @OnClose            public  void  onClose(Session  session)  {…                  …              }          …   }
  • 30.
    public  class  StockMessageDecoder  implements   Decoder.Text<StockMessage>  {          @Override        public  StockMessage  decode(String  s)  throws  DecodeException  {                Gson  gson  =  new  Gson();                return  gson.fromJson(s,  StockMessage.class);        }   } public  class  StockMessageEncoder  implements   Encoder.Text<StockMessage>  {                  @Override          public  String  encode(StockMessage  object)  throws   EncodeException  {                  return  new  Gson().toJson(object);          }   } Decoder Encoder
  • 31.
    Heartbeats: Pings andPongs Heartbeats is a mechanism to check that connection is still alive. If a WebSocket implementation receives a ping it has to respond with a pong ASAP. There is no JavaScript API to support Pings and Pongs.
  • 32.
  • 33.
    The Software The Server: GlassFish4 (promoted build B88), Oracle The Java app can generate random price quotes The Client: HTML5 Ext JS framework, Sencha The Chrome Browser, Google Charles proxy Introducing the WebSocket Demo The Goal 1. The server generates random stock prices for a 12-stock portfolio 2. The server pushes 20 price quotes per second using WebSocket 3. The HTML client shows the prices in a data grid
  • 34.
    What was Pushedvia WebSocket The size of each data push: The length price quote data: from 42 to 45 bytes WebSocket added overhead: 2 bytes {"symbol":  "APPL",  "id":  555,  "price":  "451.29"}  
  • 35.
    Comparing Overheads Http Request-Response Eachroundtrip has: request header: 429 bytes + 268 bytes response header + 46 bytes data Server-Sent Events Initial request header 406 bytes + 268 bytes response header Each push: 46 bytes data + 8 bytes message wrapper WebSocket Initial upgrade request: 406 bytes + 268 bytes Each push 46 bytes data + 2 bytes message wrapper It’s full-duplex, not HTTP-based, works much faster.
  • 36.
  • 37.
    Reducing kilobytes ofdata to 2 bytes... and reducing latency from 150ms to 50 ms is far more than marginal. In fact, these two factors alone are enough to make WebSocket seriously interesting to Google. Ian Hickson, Google The lead HTML5 writer
  • 38.
    Where to useWebSockets - Live trading/sports ticker - Controlling medical equipment over the web - Chat applications - Multiplayer online games - Real-time updating social streams
  • 39.
    Unedited drafts ofthe book Enterprise Web Development are published at enterprisewebbook.com
  • 40.
    Contact Info Farata Systems:faratasystems.com Personal blog: yakovfain.com Twitter: @yfain Podcasts in Russian: americhka.us Thank you!