websockets on the jvm: atmosphere to the rescue!
DESCRIPTION
WebSockets' State of the Union on the JVM with the help of the Atmosphere Framework. Status of atmosphere.js, socketio.js, socks.js client side library discussed as well.TRANSCRIPT
@jfarcand@atmo_framework
WebSockets on the JVM: Atmosphere to the rescue!
@jfarcandAsync-IO.org, http://async-io.orgCTO
@asyncio@atmo_framework
WebSockets: Myth or Reality
WebSocket a standard? Really?
Tricks for Production Ready WebSocket Application
@asyncio@atmo_framework
is a web technology providing for bi-directional, full-duplex communications channels over a single TCP connection.
WebSockets
Browser Serverrequest
empty response
request
response
event
request
request
responseevent
request
response part
event
response part
event
Polling Long Polling StreamingBrowser
Server
Browser Server
request
responseevent
event
Browser Server
request
Response
event
response
event
WebSocket
@asyncio@atmo_framework
Agenda
• Who I am• Atmosphere• WebSockets: Serveur Side• WebSocket: Client Side• Socket.IO and Socks.js• Conclusion
@asyncio@atmo_framework
Who is this strange accent guy• Creator of Frameworks
• Grizzly (NIO Framework)
• AsyncHttpClient (HTTP/WebSocket)
• Atmosphere Framework
• Ex-GlassFish, Tomcat & Jetty « committer »
• Ex Sun Microsystem, France Telecom, Ning, Sonatype
@asyncio@atmo_framework
Currently• Founder anf CTO Async-
IO.org
• Since 2013• Atmosphere Support,
Atmosphere Pro & Elite
• Atmosphere is still licenced Apache 2
@asyncio@atmo_framework
Async-IO
blank slidefor your own
pictures
WebSockets: Server Side
@asyncio@atmo_framework
WebSockets: Server Side
• Tomcat 7+, Jetty 7+, GlassFish 3+, Resin 2+, JBoss 7.1.2+ native Websocket API
• Non Portable, Unstable API
• Missing Functionality
• What the point of Atmosphere then???????
blank slidefor your own
pictures
@asyncio@atmo_framework
WebSockets: Server Side
AtmosphereTo
The Rescue!
@asyncio@atmo_framework
Atmosphere: github.com/Atmosphere• Portable, Stable, EVERYWHERE
• Use Native API or JSR 356
• Javascript API(s) & Java (wAsync)
• 950+ mailing list
@asyncio@atmo_framework
JAX Awards 2014
Nominated for Most Innovative Java Technology
@asyncio@atmo_framework
Atmosphere: Well Established!!
http://www.parleys.com/
play/514892260364bc
17fc56bde7/chapter0/about
@asyncio@atmo_framework
Atmosphere: Run on top of
@asyncio@atmo_framework
atmosphere.js
• 100% Javascript Library
• Small, portable, mobile optimized
• Transparent Fallback
• One API to Rule them all: WebSockets/Server Sides Event/Long-Polling/HTML File/JSONP/Polling/Streaming etc.
@asyncio@atmo_framework
Atmosphere : Clients
@asyncio@atmo_framework
Atmosphere: Browsers
6+
3.5+
4+
3+
10+
@asyncio@atmo_framework
Atmosphere: Into the Cloud
• Your Atmosphere’s Application transparently work into the cloud
Proxy/Load balancer
@asyncio@atmo_framework
Atmosphere: Cloud
blank slidefor your own
pictures
Enterprise Ready?
@asyncio@atmo_framework
Atmosphere Pro! • atmosphere-satellite: replicate your data and code together In-
Memory for faster execution and seamless elastic scalability across your Atmosphere's Nodes/Servers.
• atmosphere-tower-control: Monitor your Atmosphere's Node/Server state via any JMX compliant tool like Java Mission Control. Get statistics on how many websocket or long-polling clients are currently connected, add/move connected clients from one node to another, for example in case of a node being restarted, etc.
• atmosphere-postman: Client/Server extension to Atmosphere to support message delivery guarantee.
http://async-io.org/AtmospherePro.html
blank slidefor your own
pictures
Fix broken browsers/servers for you!
`
Atmosphere
var socket = atmosphere; var subSocket; var request = { url: document.location.toString() + 'chat’, contentType : "application/json”, transport : "websocket" , trackMessageLength : true };
request.onOpen = function(response) { content.html($('<p>', { text: 'Atmosphere connected using ' + response.transport })); }; request.onMessage = function (response) {
var json = atmosphere.util.parseJSON(response.responseBody)…
};
request.onClose = function(response) {…};
request.onReopen = function(response) {….}
request.onTransportFailure = function(errorMsg, request) {…}
subSocket = socket.subscribe(request);
Atmosphere
@ManagedService(path = "/chat”)public class Chat { @Ready public void onReady(AtmosphereResource r) {..}
@Disconnect public void onDisconnect(AtmosphereResourceEvent e) {..}
@Message( encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class})
public Reply onMessage(Message message){return handleAndReply(message);
}}
Atmosphere
@asyncio@atmo_framework
Problem #1 – Fallback
• Browser• Old version
• No support (IE 9)
• Proxys• Disconnect, block
• Load Balancer• Breaks Session Affinity
@asyncio@atmo_framework
Fallback with Atmosphere
public void onReady(AtmosphereResource r) { switch (r.transport()) {
case WEBSOCKET:case SSE:case LONG-POLLING:case STREAMING:case JSONP:case POLLING:
r.write(“Atmosphere is cool”);}
}
@asyncio@atmo_framework
Atmosphere I/O
• WebSocket uses « non blocking I/O »
• Fallback simulates « non blocking I/O>
Browsers behave the same way as packet are delivered uniformely.
@asyncio@atmo_framework
Problem #2 –Protocol
Tomcat 7.x/8.x => RFC 6455 (Version
13) !!<= Safari 5.0.x
Jetty9.1+ => Hixie 76 <= Safari 5.0.x
@asyncio@atmo_framework
Protocol Version
• Atmosphere => Transparent Fallback in case the protocol is not supported
@asyncio@atmo_framework
Problem #3 – Lost Lost Lost
• I/O error, websocket message will be lost.
• Message must be put inside a cache
• Message must be discarted if the connection never come back.
@asyncio@atmo_framework
Atmosphere – Messages Lost
@ManagedService(path = "/chat”)public class Chat {
@Message public void onMessage(AtmosphereResource r, String message){ // Transparently cache the message in case of a problem
// Transparently write it back once reconnected r.write(message); }}
@asyncio@atmo_framework
Problem #4 – Proxy
• Existing Proxy are cutting the connection.
• Must recover transparently from a disconnect.
• Heartbeat saves your day!
@asyncio@atmo_framework
Atmosphere – Proxy ? No Problem!
@asyncio@atmo_framework
Problem #5 – New Spec!
• Difference between Tomcat, Undertow and Jetty already!
• Spec is young, missing fondamental concepts.
• Tomcat 8.0.12 is stable. GlassFish 4 is dead, Jetty 9.1.3 stable, Undertow 1.1.Final is stable, WelLogic is broken.
@asyncio@atmo_framework
Problem #5 – JSR 356: Tomcat vs Jetty
@Message public void onMessage(String m, Session s) {
for (Session s: sessions.getOpenSessions()){ // Tomcat will throw an IllegalStateException
// if more than one thread call sendXXX.// Jetty won’t!
session.getAsyncRemote().sendText(m);}
}
@asyncio@atmo_framework
Problem #5 – JSR 356: Tomcat vs Jetty private final Semaphore semaphore = new Semaphore(1, true);
@Message public void onMessage(String m, Session s) {
for (Session s: sessions.getOpenSessions()){try{ semaphore.acquireUninterruptibly();
session.getAsyncRemote().sendText(m, …);}finally {
semaphore.release();}
}}
@asyncio@atmo_framework
Problem #5 – Tomcat vs Jetty
• I/O Event aren’t delivered the same way.
• CloseReason not the same depending on the server!!!!
// Hack if (closeCode == 1000 &&
getContainerName().contains("Tomcat")) { closeCode = 1005;
}
@asyncio@atmo_framework
Firefox is the new Internet Explorer 6!
// Reload a tab/window // Firefox => 1001 // Chrome => 1000
if (isFirefox && c.getCode() == CloseReason.GOING_AWAY
|| c.getCode() == CloseReason.NO_STATUS_CODE) {...
}
@asyncio@atmo_framework
Firefox is the new Internet Explorer 6!
Reload in Firefox produces phantom websockets connections!!!!!!!
@asyncio@atmo_framework
Problem #6 – Closing Code
• How to detect if a websocket was closed by a Proxy, a Load Balancer or the Browser!!
@asyncio@atmo_framework
Hell exists!!
@asyncio@atmo_framework
Atmosphere
@Disconnect public void onClose(AtmosphereResourceEvent e) {
if (e.isClosedByClient()){
} else if (e.isClosedByApplication()) {
} else if (e.isUnexpectlyClosed()) {
}
@asyncio@atmo_framework
blank slidefor your own
pictures
WebSockets: Client Side
@asyncio@atmo_framework
WebSockets: Javascript API
• W3C SpecificationWebSocket ws = new WebSocket(“ws://127.0.0.1:8080”);
ws.onopen = function (message) {};
ws.onclose = function (message) {};
ws.onmessage = function (message) {}
@asyncio@atmo_framework
WebSocket: Supported Browsers
10+
10+
6+
5+
10+
?
2+
@asyncio@atmo_framework
W3C API
Ohhhhhhhhh• Pong/Ping: No exposed
• Headers: Can’t add/read
• Query String: No API, append them to the url
• Cookie: Impossible to read them
@asyncio@atmo_framework
Atmosphere: First request
• W3C SpecificationT 127.0.0.1:52212 -> 127.0.0.1:8080 [AP]GET /chat?X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=2.2.0-javascript&X-Atmosphere-Transport=websocket&X-Atmosphere-TrackMessageSize=true&X-Cache-Date=0&Content-Type=application/json&X-atmo-protocol=true HTTP/1.1.Upgrade: websocket.Connection: Upgrade.Host: 127.0.0.1:8080.Origin: http://127.0.0.1:8080.Sec-WebSocket-Key: TCBKrAyFFwW8HUXqLpj2wg==.Sec-WebSocket-Version: 13.Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits, x-webkit-deflate-frame.
@asyncio@atmo_framework
Atmosphere: First request
• W3C Specification
T 127.0.0.1:8080 -> 127.0.0.1:52212 [AP]HTTP/1.1 101 Switching Protocols.X-Atmosphere-first-request: true.X-Atmosphere-tracking-id: bb7aeeb7-9e44-4aa9-848d-14bba6532de9.Connection: Upgrade.Sec-WebSocket-Accept: 5mHABDQFkWBtjoinv3lj+6dVwHg=.Upgrade: WebSocket.
@asyncio@atmo_framework
Problem #1 – onclose
• Firefox will execute the ws.onclose function on F5 (reload)
• Chrome/Safari ….. NOT!
@asyncio@atmo_framework
Problem #1: onclose
• W3C Specificationws.onclose = function(closeReason) {
switch(closeReason.code) {case 1000:
….}
// Firefox will execute this logic on every reload// closeReason.wasClean unreliableif (dirtyClose) {
reconnectTo(…); }}
@asyncio@atmo_framework
Atmosphere: onclose
• W3C Specification
// Hahahaha go to hell Firefox!!atmosphere.onClose = function
(callback) {
};
@asyncio@atmo_framework
Problem #2 – Messages’ length
• Messages can be truncated when the server write them
• The Browser received them in two chunks or I/O operation
@asyncio@atmo_framework
Problem #2: Messages’ length
• W3C Specification
// Craaaaaaashhhhhhhhhhhhhws.onmessage = function (callback) {
window.JSON.parse(callback.data);};
@asyncio@atmo_framework
Problem #2: What’s Up, Doctor!
• W3C Specification
// atmosphere.js transparently // handles it
atmosphere.onMessage = function (callback) {window.JSON.parse(callback.responseBody);
};
@asyncio@atmo_framework
Problem #3 – UUID and Cookie
• No Cookie to identify the client
@asyncio@atmo_framework
Problem #3 – UUID and Cookie
• Solutions:
• Normal request => set-cookie
• Next WebSockets
• Useful if you deploy on Amazon
(1) HTTP
(2) websockets
@asyncio@atmo_framework
Atmosphere: Handshake Protocol
• W3C Specification
T 127.0.0.1:8080 -> 127.0.0.1:52212 [AP].651|bb7aeeb7-9e44-4aa9-848d-14bba6532de9|1397426374301|
T 127.0.0.1:8080 -> 127.0.0.1:52212 [AP]..
T 127.0.0.1:8080 -> 127.0.0.1:52212 [AP]..
@asyncio@atmo_framework
Problem #4 – Proxy
• Proxy can close the websocket or completely ignore the websocket handshake, completely fooling the server…and the browser!
• ws.open never called!!!!
@asyncio@atmo_framework
Problem #4 – Proxy
• W3C Specification
websocket.onopen = function (message) { alert(“Ha ha ha good luck!!!”);}
websocket.onmessage = function(message) { // Never called
alert(“Sleep on it!!!”);}
@asyncio@atmo_framework
Problem #4 – Proxy
• W3C Specification
atmosphere.onReopen = function (callback) { // Yeahhhh!!!
};
atmosphere.onTransportFailure = function (callback) { // Apache Proxy, I hate you!!!
};
@asyncio@atmo_framework
Hazelcast
@asyncio@atmo_framework
blank slidefor your own
pictures
SockJs et Socket.ioSocket.IO and Socks.js
@asyncio@atmo_framework
Socket.IO• For Node.js
• Streaming, Long-Polling and now Websocket
• Extremely popular, lot of »issues »
602 => https://github.com/LearnBoost/socket.io/issues
• Supported by Atmosphere, both client and server side. Replace node.js
@asyncio@atmo_framework
Socket.IO - Atmosphere
• W3C Specification@ManagedService(path = "/chat”)public class Chat { @Ready public void onReady(AtmosphereResource r) {..}
@Disconnect public void onDisconnect(AtmosphereResourceEvent e) {..}
@Message( encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class})
public Reply onMessage(Message message){return handleAndReply(message);
}}
@asyncio@atmo_framework
Socket.IO - Atmosphere
• W3C Specificationvar socket = io.connect('', {'resource’:document.location.toString() + 'chat'});
socket.on('connect', function () { content.html($('<p>',
{ text:'Atmosphere connected using this.socket.transport.name}));});
socket.on('chat message', function() { var json = jQuery.parseJSON(msg);
… });
socket.on(‘error’, function (e) {…});
@asyncio@atmo_framework
Socks.js
• For Node.js
• Emulate WebSocket, with « fallback »
• Socks.js popularity increase, due to Spring 4, Vert.x and Atmosphere support
@asyncio@atmo_framework
Socks.js - Atmosphere
• W3C Specification@ManagedService(path = "/clavarder”)public class Chat { @Ready public void onReady(AtmosphereResource r) {..}
@Disconnect public void onDisconnect(AtmosphereResourceEvent e) {..}
@Message( encoders = {JacksonEncoder.class}, decoders = {JacksonDecoder.class})
public Reply onMessage(Message message){return handleAndReply(message);
}}
@asyncio@atmo_framework
Socks.js- Atmosphere
• W3C Specificationvar socket = new SockJS('http://' + window.location.host + '/chat', null, { 'protocols_whitelist': ['websocket', 'xhr-streaming', 'iframe-eventsource', 'iframe-htmlfile', 'xhr-polling', 'jsonp-polling'] });
socket.onopen = function() { content.html($('<p>', { text: 'Atmosphere connected using SockJs client'}));}
socket.onmessage = function (response) { var message = response.data;
var json = JSON.parse(message);}
socket.onclose = function() {}
@asyncio@atmo_framework
Conclusion
• WebSockets in Prod without fallback? You’re Crazy!!
• Atmosphere is production ready, and fixes all the issues listed here!
• What are you waiting for?
@asyncio@atmo_framework
@All images copyright JeanFrancois Arcand
https://www.linkedin.com/in/jfarcand
http://async-io.org
http://github.com/Atmosphere/atmosphere
http://github.com/jfarcand
Credits