SlideShare une entreprise Scribd logo
1  sur  98
Télécharger pour lire hors ligne
Building Real-Time Apps with
EmberJS & WebSockets
Ben LimmerGEMConf - 5/21/2016 ember.party
blimmer
Ben LimmerEmberJS Meetup - 2/24/2016 ember.party
♥
Ben LimmerGEMConf - 5/21/2016 ember.party
Talk Roadmap
• WebSockets vs. AJAX
• Fundamentals of WebSockets
• Code!
• Other Considerations
Ben LimmerGEMConf - 5/21/2016 ember.party
request
response
request
response
AJAX
Ben LimmerGEMConf - 5/21/2016 ember.party
with a lot of apps, this
paradigm still works
Ben LimmerGEMConf - 5/21/2016 ember.party
but what about
real-time apps?
Ben LimmerGEMConf - 5/21/2016 ember.party
e.g.
Ben LimmerGEMConf - 5/21/2016 ember.party
live dashboards
Ben LimmerGEMConf - 5/21/2016 ember.party
Source: http://www.heckyl.com/
Ben LimmerGEMConf - 5/21/2016 ember.party
2nd screen apps
Ben LimmerGEMConf - 5/21/2016 ember.party© MLB / Source: MLB.com
Ben LimmerGEMConf - 5/21/2016 ember.party
deployment
notifications
Ben LimmerGEMConf - 5/21/2016 ember.party
Source: inbox.google.com
Ben LimmerGEMConf - 5/21/2016 ember.party
games
Ben LimmerGEMConf - 5/21/2016 ember.party
Source: http://browserquest.mozilla.org/img/common/promo-title.jpg
Ben LimmerGEMConf - 5/21/2016 ember.party
chat
gamesdeployment
notifications
live dashboards
2nd screen
apps
activity
streams
comment
sections
realtime
progress
collaborative
editing
Ben LimmerGEMConf - 5/21/2016 ember.party
how do we build a
real-time app?
Ben LimmerGEMConf - 5/21/2016 ember.party
update?
nope.
(old way)
short polling
update?
nope.
data
update?
yep!
Ben LimmerGEMConf - 5/21/2016 ember.party
(old way)
long polling
request
Keep-Alive
timeout
request
Keep-Alive
data
response
request
Keep-Alive
Ben LimmerGEMConf - 5/21/2016 ember.party
WebSockets
handshake
connection opened
bi-directional
communication
Ben LimmerGEMConf - 5/21/2016 ember.party
WebSockets
no polling
full duplex over TCP
communication over
standard HTTP(S) ports
broadcast to all
connected clients
Ben LimmerGEMConf - 5/21/2016 ember.party
Talk Roadmap
• WebSockets vs. AJAX
• Fundamentals of WebSockets
• Code!
• Other Considerations
Ben LimmerGEMConf - 5/21/2016 ember.party
the handshake
Ben LimmerGEMConf - 5/21/2016 ember.party
Request
GET wss://example.org/socket HTTP/1.1
Origin: https://example.org
Host: example.org
Sec-WebSocket-Key: zy6Dy9mSAIM7GJZNf9rI1A==
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Response
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Sec-WebSocket-Accept: EDJa7WCAQQzMCYNJM42Syuo9SqQ=
Upgrade: websocket
events
• open
• message
• error
• close
• send
• close
methods
Ben LimmerGEMConf - 5/21/2016 ember.party
WebSocket.send()
Ben LimmerGEMConf - 5/21/2016 ember.party
send(String 'foo');
send(Blob 010101);
send(ArrayBuffer file);
Ben LimmerGEMConf - 5/21/2016 ember.party
WebSocket.send(’YOLO’);
Ben LimmerGEMConf - 5/21/2016 ember.party
sub-protocols
• a contract between client/server
• 2 classes of sub-protocols
• well-defined (e.g. STOMP, WAMP)
• application specific protocols
Ben LimmerGEMConf - 5/21/2016 ember.party
Request with Protocol
GET wss://example.org/socket HTTP/1.1
Origin: https://example.org
Host: example.org
Sec-WebSocket-Key: zy6Dy9mSAIM7GJZNf9rI1A==
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: v10.stomp
Ben LimmerGEMConf - 5/21/2016 ember.party
STOMP
SEND
destination:/queue/a
hello queue a
^@
MESSAGE
destination:/queue/a
message-id: <message-identifier>
hello queue a
^@
Ben LimmerGEMConf - 5/21/2016 ember.party
STOMP
SEND
destination:/queue/a
hello queue a
^@
Ben LimmerGEMConf - 5/21/2016 ember.party
subprotocols bring
structure to ws
Ben LimmerGEMConf - 5/21/2016 ember.party
Talk Roadmap
• AJAX vs. WebSockets
• Fundamentals of WebSockets
• Code!
• Other Considerations
Ben LimmerGEMConf - 5/21/2016 ember.party
let’s build something!
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerEmberJS Meetup - 2/24/2016 ember.party
alice clicks
bob / everyone
sees
Ben LimmerEmberJS Meetup - 2/24/2016 ember.party
bob clicks
alice / everyone
sees
Ben LimmerGEMConf - 5/21/2016 ember.party
npm install ws
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
• fast
• simple WebSocket implementation
• few bells and whistles
npm install ws
Ben LimmerGEMConf - 5/21/2016 ember.party
server/index.js
1 const WebSocketServer = require('ws').Server;
2
3 const wss = new WebSocketServer({
4 port: process.env.PORT
5 });
Ben LimmerGEMConf - 5/21/2016 ember.party
waiting for socket
connection…
Ben LimmerGEMConf - 5/21/2016 ember.party
ember install ember-websockets
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
ember install ember-websockets
• integrates with the Ember runloop
• is an Ember.ObjectProxy
• abstracts away the WebSocket
Ben LimmerGEMConf - 5/21/2016 ember.party
app/services/rt-ember-socket.js
1 websockets: service(),
2
3 init() {
4 this._super(...arguments);
5
6 const socket = this.get('websockets').socketFor(host);
7
8 socket.on('open', this.open, this);
9 socket.on('close', this.reconnect, this);
10 },
11
12 online: false,
13 open() {
14 this.set('online', true);
15 },
16
17 reconnect() {
18 this.set('online', false);
19
20 Ember.run.later(this, () => {
21 this.get('socket').reconnect();
22 }, 5000);
23 },
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
rtember-1.0 - sub-protocol
Ben LimmerGEMConf - 5/21/2016 ember.party
rtember-1.0 - sub-protocol
data events
Ben LimmerGEMConf - 5/21/2016 ember.party
server/index.js
1 const WebSocketServer = require('ws').Server;
2
3 const wss = new WebSocketServer({
4 port: process.env.PORT,
5 handleProtocols: function(protocol, cb) {
6 const supportedProtocol =
7 protocol[protocol.indexOf('rtember-1.0')];
8 if (supportedProtocol) {
9 cb(true, supportedProtocol);
10 } else {
11 cb(false);
12 }
13 },
14 });
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
app/services/rt-ember-socket.js
1 websockets: Ember.inject.service(),
2
3 socket: null,
4 init() {
5 this._super(...arguments);
6
7 const socket = this.get('websockets')
8 .socketFor(host, ['rtember-1.0']);
9
10 socket.on('open', this.open, this);
11 socket.on('close', this.reconnect, this);
12
13 this.set('socket', socket);
14 },
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
rtember-1.0 - sub-protocol
data events
Ben LimmerGEMConf - 5/21/2016 ember.party
rtember-1.0 - sub-protocol
{
"frameType": "event",
"payload": {
“eventType": ... event type ...,
"eventInfo": ... event info ...
}
}
{
"frameType": "data",
"payload": {
... json api payload ...
}
}
or
Ben LimmerGEMConf - 5/21/2016 ember.party
rtember-1.0 - sub-protocol
data events
Ben LimmerGEMConf - 5/21/2016 ember.party
1 wss.on('connection', function(ws) {
2 sendInitialGifs(ws);
3 });
4
5 function sendInitialGifs(ws) {
6 const gifs = gifDb;
7 const random = _.sampleSize(gifs, 25);
8
9 sendDataToClient(ws, serializeGifs(random));
10 }
11
12 function sendDataToClient(ws, payload) {
13 const payload = {
14 frameType: FRAME_TYPES.DATA,
15 payload,
16 }
17 ws.send(JSON.stringify(payload));
18 }
Ben LimmerGEMConf - 5/21/2016 ember.party
app/services/rt-ember-socket.js
1 init() {
2 ...
3 socket.on('message', this.handleMessage, this);
4 ...
5 },
6
7 handleMessage(msg) {
8 const { frameType, payload } = JSON.parse(msg.data);
9
10 if (frameType === FRAME_TYPES.DATA) {
11 this.handleData(payload);
12 } else {
13 warn(`Encountered unknown frame type: ${frameType}`);
14 }
15 },
16
17 handleData(payload) {
18 this.get('store').pushPayload(payload);
19 }
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
{
"frameType": "data",
"payload": {
"data": [{
"type": "gif",
"id": "3o8doPV2heuYjdN2Fy",
"attributes": {
"url": "http://giphy.com/3o8doPV2heuYjdN2Fy/giphy.gif"
}
}, { ... }, { ... }]
}
}
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
app/routes/index.js
1 export default Ember.Route.extend({
2 model() {
3 return this.store.peekAll('gif');
4 }
5 });
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
app/templates/index.hbs
{{gif-tv gifs=model}}
app/templates/components/gif-tv.hbs
<div class='suggestions'>
{{#each gifs as |gif|}}
<img src={{gif.url}} />
{{/each}}
</div>
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
rtember-1.0 - sub-protocol
data events
Ben LimmerGEMConf - 5/21/2016 ember.party
rtember-1.0 - sub-protocol
{
"frameType": "event",
"payload": {
“eventType": ... event type ...,
"eventInfo": ... event info ...
}
}
Ben LimmerGEMConf - 5/21/2016 ember.party
Share GIF Event
{
"frameType": "event",
"payload": {
"eventType": "share_gif",
"eventInfo": "<gif_id>"
}
}
{
"frameType": "data",
"payload": {[
<shared_gif>,
<previously_shared_gif>
]}
}
Ben LimmerGEMConf - 5/21/2016 ember.party
{
"frameType": "data",
"payload": {
"data": [
{
"type": "gifs",
"id": "3o8doPV2heuYjdN2Fy",
"attributes": {
"url": "http://giphy.com/3o8doPV2heuYjdN2Fy/giphy.gif",
"shared": false
}
},
{
"type": "gifs",
"id": "xTiQyBOIQe5cgiyUPS",
"attributes": {
"url": "http://giphy.com/xTiQyBOIQe5cgiyUPS/giphy.gif",
"shared": true
}
}
]
}
}
Ben LimmerGEMConf - 5/21/2016 ember.party
app/templates/components/gif-tv.hbs
<div class='suggestions'>
{{#each gifs as |gif|}}
<img {{action shareGif gif}} src={{gif.url}} />
{{/each}}
</div>
app/components/gif-tv.js
1 export default Ember.Component.extend({
2 rtEmberSocket: service(),
3
4 shareGif(gif) {
5 this.get('rtEmberSocket')
6 .sendEvent(EVENTS.SHARE_GIF, gif.get('id'));
7 },
8 });
Ben LimmerGEMConf - 5/21/2016 ember.party
6 .sendEvent(EVENTS.SHARE_GIF, gif.get('id'));
7 },
8 });
app/services/rt-ember-socket.js
1 sendEvent(eventType, eventInfo) {
2 this.get('socket').send(JSON.stringify({
3 frameType: FRAME_TYPES.EVENT,
4 payload: {
5 eventType,
6 eventInfo,
7 },
8 }));
9 }
Ben LimmerEmberJS Meetup - 2/24/2016 ember.party
1 ws.on('message', function(rawData) {
2 const data = JSON.parse(rawData);
3
4 if (data.frameType === FRAME_TYPES.EVENT) {
5 const newShare = _.find(gifDb, {
6 id: data.payload.eventInfo
7 });
8 const previouslyShared = _.find(gifDb, 'shared');
9
10 newShare.shared = true;
11 previouslyShared.shared = false;
12
13 const framePayload = {
14 frameType: FRAME_TYPES.DATA,
15 payload: serializeGifs([previouslyShared, newShare]),
16 };
17 const rawPayload = JSON.stringify(framePayload);
18 wss.clients.forEach((client) => {
19 client.send(rawPayload);
20 });
21 }
22 });
Ben LimmerGEMConf - 5/21/2016 ember.party
beware
Ben LimmerGEMConf - 5/21/2016 ember.party
1 ws.on('message', function(rawData) {
2 try {
3 const data = JSON.parse(rawData);
4
5 if (data.frameType === FRAME_TYPES.EVENT) {
6 if (data.payload.eventType !== EVENTS.SHARE_GIF) {
7 throw Error(); // unknown event
8 }
9
10 const newShare = ...;
11 if (!newShare) {
12 throw Error(); // unknown gif
13 }
14 ...
15 }
16 } catch(e) {
17 ws.close(1003); // unsupported data
18 }
19 });
Ben LimmerGEMConf - 5/21/2016 ember.party
{
"frameType": "data",
"payload": {
"data": [
{
"type": "gifs",
"id": "3o8doPV2heuYjdN2Fy",
"attributes": {
"url": "http://giphy.com/3o8doPV2heuYjdN2Fy/giphy.gif",
"shared": false
}
},
{
"type": "gifs",
"id": "xTiQyBOIQe5cgiyUPS",
"attributes": {
"url": "http://giphy.com/xTiQyBOIQe5cgiyUPS/giphy.gif",
"shared": true
}
}
]
}
}
Ben LimmerGEMConf - 5/21/2016 ember.party
app/templates/components/gif-tv.hbs
app/components/gif-tv.js
1 export default Ember.Component.extend({
2 sharedGif: computed('gifs.@each.shared', function() {
3 return this.get('gifs').findBy('shared', true);
4 }),
5 });
<div class='shared-gif'>
<img src={{sharedGif.url}} />
</div>
<div class='suggestions'>
<!-- ... -->
</div>
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
ember.party/gemconf
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
Talk Roadmap
• AJAX vs. WebSockets
• Fundamentals of WebSockets
• Code!
• Other Considerations
Ben LimmerGEMConf - 5/21/2016 ember.party
other considerations
• security
• websocket support (libraries)
• learn from example
Ben LimmerGEMConf - 5/21/2016 ember.party
security
• Use TLS (wss:// vs. ws://)
• Verify the Origin header
• Verify the request by using a random token on
handshake
source: WebSocket (Andrew Lombardi) - O’Reilly
Ben LimmerGEMConf - 5/21/2016 ember.party
other considerations
• security
• websocket support (libraries)
• learn from example
Ben LimmerGEMConf - 5/21/2016 ember.party
support
Ben LimmerGEMConf - 5/21/2016 ember.party
9
Ben LimmerGEMConf - 5/21/2016 ember.party
socket.io
• Graceful fallback to polling / flash (!)
• Syntactic Sugar vs. ws package
• Support in ember-websockets add-on
Ben LimmerGEMConf - 5/21/2016 ember.party
Ben LimmerGEMConf - 5/21/2016 ember.party
pusher.com
• Graceful fallback
• Presence support
• Authentication / Security strategies
• No infrastructure required
Ben LimmerGEMConf - 5/21/2016 ember.party
other considerations
• security
• websocket support (libraries)
• learn from example
Ben LimmerGEMConf - 5/21/2016 ember.party
learn by example
Ben LimmerGEMConf - 5/21/2016 ember.party
learn by example
Ben LimmerGEMConf - 5/21/2016 ember.party
https://github.com/blimmer/real-time-ember-client
https://github.com/blimmer/real-time-ember-server
l1m5blimmer
Ben LimmerGEMConf - 5/21/2016 ember.party
thanks!
• WebSocket: Lightweight Client-Server
Communications (O’Reilly)
• WebSockets: Methods for Real-Time Data
Streaming (Steve Schwartz)
Credits
• pusher.com
• socket.io
• node ws
• websocket security (heroku)
Resources

Contenu connexe

Tendances

Biotech guide3rdedition (2)
Biotech guide3rdedition (2)Biotech guide3rdedition (2)
Biotech guide3rdedition (2)
Food Insight
 

Tendances (12)

不會 Javascript 沒關係,用 Blazor 來解決前端需求 - 成為 Full Stack .NET 開發者吧 - .NET Conf 2020...
不會 Javascript 沒關係,用 Blazor 來解決前端需求 - 成為 Full Stack .NET 開發者吧 - .NET Conf 2020...不會 Javascript 沒關係,用 Blazor 來解決前端需求 - 成為 Full Stack .NET 開發者吧 - .NET Conf 2020...
不會 Javascript 沒關係,用 Blazor 來解決前端需求 - 成為 Full Stack .NET 開發者吧 - .NET Conf 2020...
 
Introduction to VueJS & Vuex
Introduction to VueJS & VuexIntroduction to VueJS & Vuex
Introduction to VueJS & Vuex
 
Next.js vs React | what to choose for frontend development_
Next.js vs React | what to choose for frontend development_Next.js vs React | what to choose for frontend development_
Next.js vs React | what to choose for frontend development_
 
State management
State managementState management
State management
 
Overview of github
Overview of githubOverview of github
Overview of github
 
JavaScript Fetch API
JavaScript Fetch APIJavaScript Fetch API
JavaScript Fetch API
 
Hybrid Mobile Development with Apache Cordova and Java EE 7 (JavaOne 2014)
Hybrid Mobile Development with Apache Cordova and Java EE 7 (JavaOne 2014)Hybrid Mobile Development with Apache Cordova and Java EE 7 (JavaOne 2014)
Hybrid Mobile Development with Apache Cordova and Java EE 7 (JavaOne 2014)
 
API Design, A Quick Guide to REST, SOAP, gRPC, and GraphQL, By Vahid Rahimian
API Design, A Quick Guide to REST, SOAP, gRPC, and GraphQL, By Vahid RahimianAPI Design, A Quick Guide to REST, SOAP, gRPC, and GraphQL, By Vahid Rahimian
API Design, A Quick Guide to REST, SOAP, gRPC, and GraphQL, By Vahid Rahimian
 
CSS ppt
CSS pptCSS ppt
CSS ppt
 
Building Reusable SwiftUI Components
Building Reusable SwiftUI ComponentsBuilding Reusable SwiftUI Components
Building Reusable SwiftUI Components
 
Biotech guide3rdedition (2)
Biotech guide3rdedition (2)Biotech guide3rdedition (2)
Biotech guide3rdedition (2)
 
Suita de tehnologii HTML5
Suita de tehnologii HTML5Suita de tehnologii HTML5
Suita de tehnologii HTML5
 

En vedette

Ember.js internals backburner.js and rsvp.js
Ember.js internals  backburner.js and rsvp.jsEmber.js internals  backburner.js and rsvp.js
Ember.js internals backburner.js and rsvp.js
gavinjoyce
 

En vedette (8)

Ember and WebSockets
Ember and WebSocketsEmber and WebSockets
Ember and WebSockets
 
Ember js meetup treviso liquid-fire
Ember js meetup treviso liquid-fireEmber js meetup treviso liquid-fire
Ember js meetup treviso liquid-fire
 
HTML5 Websockets and Java - Arun Gupta
HTML5 Websockets and Java - Arun GuptaHTML5 Websockets and Java - Arun Gupta
HTML5 Websockets and Java - Arun Gupta
 
Real-Time Web Apps in 2015 & Beyond
Real-Time Web Apps in 2015 & BeyondReal-Time Web Apps in 2015 & Beyond
Real-Time Web Apps in 2015 & Beyond
 
Ember.js internals backburner.js and rsvp.js
Ember.js internals  backburner.js and rsvp.jsEmber.js internals  backburner.js and rsvp.js
Ember.js internals backburner.js and rsvp.js
 
Parse Apps with Ember.js
Parse Apps with Ember.jsParse Apps with Ember.js
Parse Apps with Ember.js
 
Facebook UI Evolution
Facebook UI EvolutionFacebook UI Evolution
Facebook UI Evolution
 
Introduction to WAMP, a protocol enabling PUB/SUB and RPC over Websocket
Introduction to WAMP, a protocol enabling PUB/SUB and RPC over WebsocketIntroduction to WAMP, a protocol enabling PUB/SUB and RPC over Websocket
Introduction to WAMP, a protocol enabling PUB/SUB and RPC over Websocket
 

Similaire à Building Realtime Apps with Ember.js and WebSockets

WebSocket JSON Hackday
WebSocket JSON HackdayWebSocket JSON Hackday
WebSocket JSON Hackday
Somay Nakhal
 
Bfg Ploneconf Oct2008
Bfg Ploneconf Oct2008Bfg Ploneconf Oct2008
Bfg Ploneconf Oct2008
Jeffrey Clark
 

Similaire à Building Realtime Apps with Ember.js and WebSockets (20)

Going Live! with Comet
Going Live! with CometGoing Live! with Comet
Going Live! with Comet
 
Hitchhiker's guide to the front end development
Hitchhiker's guide to the front end developmentHitchhiker's guide to the front end development
Hitchhiker's guide to the front end development
 
Top-5-production-devconMunich-2023.pptx
Top-5-production-devconMunich-2023.pptxTop-5-production-devconMunich-2023.pptx
Top-5-production-devconMunich-2023.pptx
 
The curious case of misrendered JSON
The curious case of misrendered JSONThe curious case of misrendered JSON
The curious case of misrendered JSON
 
Top-5-Performance-JaxLondon-2023.pptx
Top-5-Performance-JaxLondon-2023.pptxTop-5-Performance-JaxLondon-2023.pptx
Top-5-Performance-JaxLondon-2023.pptx
 
Socket.io
Socket.ioSocket.io
Socket.io
 
WPE WebKit for Android
WPE WebKit for AndroidWPE WebKit for Android
WPE WebKit for Android
 
Time for Comet?
Time for Comet?Time for Comet?
Time for Comet?
 
스트리밍과 디지털 권리 관리
스트리밍과 디지털 권리 관리스트리밍과 디지털 권리 관리
스트리밍과 디지털 권리 관리
 
WebSocket JSON Hackday
WebSocket JSON HackdayWebSocket JSON Hackday
WebSocket JSON Hackday
 
itjsbagg410
itjsbagg410itjsbagg410
itjsbagg410
 
The HTML5 WebSocket API
The HTML5 WebSocket APIThe HTML5 WebSocket API
The HTML5 WebSocket API
 
Comet from JavaOne 2008
Comet from JavaOne 2008Comet from JavaOne 2008
Comet from JavaOne 2008
 
Bfg Ploneconf Oct2008
Bfg Ploneconf Oct2008Bfg Ploneconf Oct2008
Bfg Ploneconf Oct2008
 
Analyzing the Performance of Mobile Web
Analyzing the Performance of Mobile WebAnalyzing the Performance of Mobile Web
Analyzing the Performance of Mobile Web
 
IE 8 et les standards du Web - Chris Wilson - Paris Web 2008
IE 8 et les standards du Web - Chris Wilson - Paris Web 2008IE 8 et les standards du Web - Chris Wilson - Paris Web 2008
IE 8 et les standards du Web - Chris Wilson - Paris Web 2008
 
Web Leaps Forward
Web Leaps ForwardWeb Leaps Forward
Web Leaps Forward
 
Consuming GRIN GLOBAL Webservices
Consuming GRIN GLOBAL WebservicesConsuming GRIN GLOBAL Webservices
Consuming GRIN GLOBAL Webservices
 
WebRTC: A front-end perspective
WebRTC: A front-end perspectiveWebRTC: A front-end perspective
WebRTC: A front-end perspective
 
"Hidden difficulties of debugger implementation for .NET WASM apps", Andrii R...
"Hidden difficulties of debugger implementation for .NET WASM apps", Andrii R..."Hidden difficulties of debugger implementation for .NET WASM apps", Andrii R...
"Hidden difficulties of debugger implementation for .NET WASM apps", Andrii R...
 

Plus de Ben Limmer

Plus de Ben Limmer (9)

Tips & Tricks for Being a Successful Tech Lead
Tips & Tricks for Being a Successful Tech LeadTips & Tricks for Being a Successful Tech Lead
Tips & Tricks for Being a Successful Tech Lead
 
1-Up Your Git Skills
1-Up Your Git Skills1-Up Your Git Skills
1-Up Your Git Skills
 
Maximize your output (sans productivity shame)
Maximize your output (sans productivity shame)Maximize your output (sans productivity shame)
Maximize your output (sans productivity shame)
 
[OLD] Understanding Github PR Merge Options (1up-ing your git skills part 2)
[OLD] Understanding Github PR Merge Options (1up-ing your git skills part 2)[OLD] Understanding Github PR Merge Options (1up-ing your git skills part 2)
[OLD] Understanding Github PR Merge Options (1up-ing your git skills part 2)
 
Upgrading Ember.js Apps
Upgrading Ember.js AppsUpgrading Ember.js Apps
Upgrading Ember.js Apps
 
Fun with Ember 2.x Features
Fun with Ember 2.x FeaturesFun with Ember 2.x Features
Fun with Ember 2.x Features
 
Building a Single Page Application using Ember.js ... for fun and profit
Building a Single Page Application using Ember.js ... for fun and profitBuilding a Single Page Application using Ember.js ... for fun and profit
Building a Single Page Application using Ember.js ... for fun and profit
 
Automated Testing in EmberJS
Automated Testing in EmberJSAutomated Testing in EmberJS
Automated Testing in EmberJS
 
Deploying a Location-Aware Ember Application
Deploying a Location-Aware Ember ApplicationDeploying a Location-Aware Ember Application
Deploying a Location-Aware Ember Application
 

Dernier

Dernier (20)

Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 

Building Realtime Apps with Ember.js and WebSockets

  • 1. Building Real-Time Apps with EmberJS & WebSockets
  • 2. Ben LimmerGEMConf - 5/21/2016 ember.party blimmer
  • 3. Ben LimmerEmberJS Meetup - 2/24/2016 ember.party ♥
  • 4. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • WebSockets vs. AJAX • Fundamentals of WebSockets • Code! • Other Considerations
  • 5. Ben LimmerGEMConf - 5/21/2016 ember.party request response request response AJAX
  • 6. Ben LimmerGEMConf - 5/21/2016 ember.party with a lot of apps, this paradigm still works
  • 7. Ben LimmerGEMConf - 5/21/2016 ember.party but what about real-time apps?
  • 8. Ben LimmerGEMConf - 5/21/2016 ember.party e.g.
  • 9. Ben LimmerGEMConf - 5/21/2016 ember.party live dashboards
  • 10. Ben LimmerGEMConf - 5/21/2016 ember.party Source: http://www.heckyl.com/
  • 11. Ben LimmerGEMConf - 5/21/2016 ember.party 2nd screen apps
  • 12. Ben LimmerGEMConf - 5/21/2016 ember.party© MLB / Source: MLB.com
  • 13. Ben LimmerGEMConf - 5/21/2016 ember.party deployment notifications
  • 14. Ben LimmerGEMConf - 5/21/2016 ember.party Source: inbox.google.com
  • 15. Ben LimmerGEMConf - 5/21/2016 ember.party games
  • 16. Ben LimmerGEMConf - 5/21/2016 ember.party Source: http://browserquest.mozilla.org/img/common/promo-title.jpg
  • 17. Ben LimmerGEMConf - 5/21/2016 ember.party chat gamesdeployment notifications live dashboards 2nd screen apps activity streams comment sections realtime progress collaborative editing
  • 18. Ben LimmerGEMConf - 5/21/2016 ember.party how do we build a real-time app?
  • 19. Ben LimmerGEMConf - 5/21/2016 ember.party update? nope. (old way) short polling update? nope. data update? yep!
  • 20. Ben LimmerGEMConf - 5/21/2016 ember.party (old way) long polling request Keep-Alive timeout request Keep-Alive data response request Keep-Alive
  • 21. Ben LimmerGEMConf - 5/21/2016 ember.party WebSockets handshake connection opened bi-directional communication
  • 22. Ben LimmerGEMConf - 5/21/2016 ember.party WebSockets no polling full duplex over TCP communication over standard HTTP(S) ports broadcast to all connected clients
  • 23. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • WebSockets vs. AJAX • Fundamentals of WebSockets • Code! • Other Considerations
  • 24. Ben LimmerGEMConf - 5/21/2016 ember.party the handshake
  • 25. Ben LimmerGEMConf - 5/21/2016 ember.party Request GET wss://example.org/socket HTTP/1.1 Origin: https://example.org Host: example.org Sec-WebSocket-Key: zy6Dy9mSAIM7GJZNf9rI1A== Upgrade: websocket Connection: Upgrade Sec-WebSocket-Version: 13 Response HTTP/1.1 101 Switching Protocols Connection: Upgrade Sec-WebSocket-Accept: EDJa7WCAQQzMCYNJM42Syuo9SqQ= Upgrade: websocket
  • 26. events • open • message • error • close • send • close methods
  • 27. Ben LimmerGEMConf - 5/21/2016 ember.party WebSocket.send()
  • 28. Ben LimmerGEMConf - 5/21/2016 ember.party send(String 'foo'); send(Blob 010101); send(ArrayBuffer file);
  • 29. Ben LimmerGEMConf - 5/21/2016 ember.party WebSocket.send(’YOLO’);
  • 30. Ben LimmerGEMConf - 5/21/2016 ember.party sub-protocols • a contract between client/server • 2 classes of sub-protocols • well-defined (e.g. STOMP, WAMP) • application specific protocols
  • 31. Ben LimmerGEMConf - 5/21/2016 ember.party Request with Protocol GET wss://example.org/socket HTTP/1.1 Origin: https://example.org Host: example.org Sec-WebSocket-Key: zy6Dy9mSAIM7GJZNf9rI1A== Upgrade: websocket Connection: Upgrade Sec-WebSocket-Version: 13 Sec-WebSocket-Protocol: v10.stomp
  • 32. Ben LimmerGEMConf - 5/21/2016 ember.party STOMP SEND destination:/queue/a hello queue a ^@ MESSAGE destination:/queue/a message-id: <message-identifier> hello queue a ^@
  • 33. Ben LimmerGEMConf - 5/21/2016 ember.party STOMP SEND destination:/queue/a hello queue a ^@
  • 34. Ben LimmerGEMConf - 5/21/2016 ember.party subprotocols bring structure to ws
  • 35. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • AJAX vs. WebSockets • Fundamentals of WebSockets • Code! • Other Considerations
  • 36. Ben LimmerGEMConf - 5/21/2016 ember.party let’s build something!
  • 37. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 38. Ben LimmerEmberJS Meetup - 2/24/2016 ember.party alice clicks bob / everyone sees
  • 39. Ben LimmerEmberJS Meetup - 2/24/2016 ember.party bob clicks alice / everyone sees
  • 40. Ben LimmerGEMConf - 5/21/2016 ember.party npm install ws
  • 41. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 42. Ben LimmerGEMConf - 5/21/2016 ember.party • fast • simple WebSocket implementation • few bells and whistles npm install ws
  • 43. Ben LimmerGEMConf - 5/21/2016 ember.party server/index.js 1 const WebSocketServer = require('ws').Server; 2 3 const wss = new WebSocketServer({ 4 port: process.env.PORT 5 });
  • 44. Ben LimmerGEMConf - 5/21/2016 ember.party waiting for socket connection…
  • 45. Ben LimmerGEMConf - 5/21/2016 ember.party ember install ember-websockets
  • 46. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 47. Ben LimmerGEMConf - 5/21/2016 ember.party ember install ember-websockets • integrates with the Ember runloop • is an Ember.ObjectProxy • abstracts away the WebSocket
  • 48. Ben LimmerGEMConf - 5/21/2016 ember.party app/services/rt-ember-socket.js 1 websockets: service(), 2 3 init() { 4 this._super(...arguments); 5 6 const socket = this.get('websockets').socketFor(host); 7 8 socket.on('open', this.open, this); 9 socket.on('close', this.reconnect, this); 10 }, 11 12 online: false, 13 open() { 14 this.set('online', true); 15 }, 16 17 reconnect() { 18 this.set('online', false); 19 20 Ember.run.later(this, () => { 21 this.get('socket').reconnect(); 22 }, 5000); 23 },
  • 49. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 50. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol
  • 51. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  • 52. Ben LimmerGEMConf - 5/21/2016 ember.party server/index.js 1 const WebSocketServer = require('ws').Server; 2 3 const wss = new WebSocketServer({ 4 port: process.env.PORT, 5 handleProtocols: function(protocol, cb) { 6 const supportedProtocol = 7 protocol[protocol.indexOf('rtember-1.0')]; 8 if (supportedProtocol) { 9 cb(true, supportedProtocol); 10 } else { 11 cb(false); 12 } 13 }, 14 });
  • 53. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 54. Ben LimmerGEMConf - 5/21/2016 ember.party app/services/rt-ember-socket.js 1 websockets: Ember.inject.service(), 2 3 socket: null, 4 init() { 5 this._super(...arguments); 6 7 const socket = this.get('websockets') 8 .socketFor(host, ['rtember-1.0']); 9 10 socket.on('open', this.open, this); 11 socket.on('close', this.reconnect, this); 12 13 this.set('socket', socket); 14 },
  • 55. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 56. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  • 57. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol { "frameType": "event", "payload": { “eventType": ... event type ..., "eventInfo": ... event info ... } } { "frameType": "data", "payload": { ... json api payload ... } } or
  • 58. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  • 59. Ben LimmerGEMConf - 5/21/2016 ember.party 1 wss.on('connection', function(ws) { 2 sendInitialGifs(ws); 3 }); 4 5 function sendInitialGifs(ws) { 6 const gifs = gifDb; 7 const random = _.sampleSize(gifs, 25); 8 9 sendDataToClient(ws, serializeGifs(random)); 10 } 11 12 function sendDataToClient(ws, payload) { 13 const payload = { 14 frameType: FRAME_TYPES.DATA, 15 payload, 16 } 17 ws.send(JSON.stringify(payload)); 18 }
  • 60. Ben LimmerGEMConf - 5/21/2016 ember.party app/services/rt-ember-socket.js 1 init() { 2 ... 3 socket.on('message', this.handleMessage, this); 4 ... 5 }, 6 7 handleMessage(msg) { 8 const { frameType, payload } = JSON.parse(msg.data); 9 10 if (frameType === FRAME_TYPES.DATA) { 11 this.handleData(payload); 12 } else { 13 warn(`Encountered unknown frame type: ${frameType}`); 14 } 15 }, 16 17 handleData(payload) { 18 this.get('store').pushPayload(payload); 19 }
  • 61. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 62. Ben LimmerGEMConf - 5/21/2016 ember.party { "frameType": "data", "payload": { "data": [{ "type": "gif", "id": "3o8doPV2heuYjdN2Fy", "attributes": { "url": "http://giphy.com/3o8doPV2heuYjdN2Fy/giphy.gif" } }, { ... }, { ... }] } }
  • 63. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 64. Ben LimmerGEMConf - 5/21/2016 ember.party app/routes/index.js 1 export default Ember.Route.extend({ 2 model() { 3 return this.store.peekAll('gif'); 4 } 5 });
  • 65. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 66. Ben LimmerGEMConf - 5/21/2016 ember.party app/templates/index.hbs {{gif-tv gifs=model}} app/templates/components/gif-tv.hbs <div class='suggestions'> {{#each gifs as |gif|}} <img src={{gif.url}} /> {{/each}} </div>
  • 67. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 68. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 69. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  • 70. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol { "frameType": "event", "payload": { “eventType": ... event type ..., "eventInfo": ... event info ... } }
  • 71. Ben LimmerGEMConf - 5/21/2016 ember.party Share GIF Event { "frameType": "event", "payload": { "eventType": "share_gif", "eventInfo": "<gif_id>" } } { "frameType": "data", "payload": {[ <shared_gif>, <previously_shared_gif> ]} }
  • 72. Ben LimmerGEMConf - 5/21/2016 ember.party { "frameType": "data", "payload": { "data": [ { "type": "gifs", "id": "3o8doPV2heuYjdN2Fy", "attributes": { "url": "http://giphy.com/3o8doPV2heuYjdN2Fy/giphy.gif", "shared": false } }, { "type": "gifs", "id": "xTiQyBOIQe5cgiyUPS", "attributes": { "url": "http://giphy.com/xTiQyBOIQe5cgiyUPS/giphy.gif", "shared": true } } ] } }
  • 73. Ben LimmerGEMConf - 5/21/2016 ember.party app/templates/components/gif-tv.hbs <div class='suggestions'> {{#each gifs as |gif|}} <img {{action shareGif gif}} src={{gif.url}} /> {{/each}} </div> app/components/gif-tv.js 1 export default Ember.Component.extend({ 2 rtEmberSocket: service(), 3 4 shareGif(gif) { 5 this.get('rtEmberSocket') 6 .sendEvent(EVENTS.SHARE_GIF, gif.get('id')); 7 }, 8 });
  • 74. Ben LimmerGEMConf - 5/21/2016 ember.party 6 .sendEvent(EVENTS.SHARE_GIF, gif.get('id')); 7 }, 8 }); app/services/rt-ember-socket.js 1 sendEvent(eventType, eventInfo) { 2 this.get('socket').send(JSON.stringify({ 3 frameType: FRAME_TYPES.EVENT, 4 payload: { 5 eventType, 6 eventInfo, 7 }, 8 })); 9 }
  • 75. Ben LimmerEmberJS Meetup - 2/24/2016 ember.party 1 ws.on('message', function(rawData) { 2 const data = JSON.parse(rawData); 3 4 if (data.frameType === FRAME_TYPES.EVENT) { 5 const newShare = _.find(gifDb, { 6 id: data.payload.eventInfo 7 }); 8 const previouslyShared = _.find(gifDb, 'shared'); 9 10 newShare.shared = true; 11 previouslyShared.shared = false; 12 13 const framePayload = { 14 frameType: FRAME_TYPES.DATA, 15 payload: serializeGifs([previouslyShared, newShare]), 16 }; 17 const rawPayload = JSON.stringify(framePayload); 18 wss.clients.forEach((client) => { 19 client.send(rawPayload); 20 }); 21 } 22 });
  • 76. Ben LimmerGEMConf - 5/21/2016 ember.party beware
  • 77. Ben LimmerGEMConf - 5/21/2016 ember.party 1 ws.on('message', function(rawData) { 2 try { 3 const data = JSON.parse(rawData); 4 5 if (data.frameType === FRAME_TYPES.EVENT) { 6 if (data.payload.eventType !== EVENTS.SHARE_GIF) { 7 throw Error(); // unknown event 8 } 9 10 const newShare = ...; 11 if (!newShare) { 12 throw Error(); // unknown gif 13 } 14 ... 15 } 16 } catch(e) { 17 ws.close(1003); // unsupported data 18 } 19 });
  • 78. Ben LimmerGEMConf - 5/21/2016 ember.party { "frameType": "data", "payload": { "data": [ { "type": "gifs", "id": "3o8doPV2heuYjdN2Fy", "attributes": { "url": "http://giphy.com/3o8doPV2heuYjdN2Fy/giphy.gif", "shared": false } }, { "type": "gifs", "id": "xTiQyBOIQe5cgiyUPS", "attributes": { "url": "http://giphy.com/xTiQyBOIQe5cgiyUPS/giphy.gif", "shared": true } } ] } }
  • 79. Ben LimmerGEMConf - 5/21/2016 ember.party app/templates/components/gif-tv.hbs app/components/gif-tv.js 1 export default Ember.Component.extend({ 2 sharedGif: computed('gifs.@each.shared', function() { 3 return this.get('gifs').findBy('shared', true); 4 }), 5 }); <div class='shared-gif'> <img src={{sharedGif.url}} /> </div> <div class='suggestions'> <!-- ... --> </div>
  • 80. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 81. Ben LimmerGEMConf - 5/21/2016 ember.party ember.party/gemconf
  • 82. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 83. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • AJAX vs. WebSockets • Fundamentals of WebSockets • Code! • Other Considerations
  • 84. Ben LimmerGEMConf - 5/21/2016 ember.party other considerations • security • websocket support (libraries) • learn from example
  • 85. Ben LimmerGEMConf - 5/21/2016 ember.party security • Use TLS (wss:// vs. ws://) • Verify the Origin header • Verify the request by using a random token on handshake source: WebSocket (Andrew Lombardi) - O’Reilly
  • 86. Ben LimmerGEMConf - 5/21/2016 ember.party other considerations • security • websocket support (libraries) • learn from example
  • 87. Ben LimmerGEMConf - 5/21/2016 ember.party support
  • 88. Ben LimmerGEMConf - 5/21/2016 ember.party 9
  • 89. Ben LimmerGEMConf - 5/21/2016 ember.party socket.io • Graceful fallback to polling / flash (!) • Syntactic Sugar vs. ws package • Support in ember-websockets add-on
  • 90. Ben LimmerGEMConf - 5/21/2016 ember.party
  • 91. Ben LimmerGEMConf - 5/21/2016 ember.party pusher.com • Graceful fallback • Presence support • Authentication / Security strategies • No infrastructure required
  • 92. Ben LimmerGEMConf - 5/21/2016 ember.party other considerations • security • websocket support (libraries) • learn from example
  • 93. Ben LimmerGEMConf - 5/21/2016 ember.party learn by example
  • 94. Ben LimmerGEMConf - 5/21/2016 ember.party learn by example
  • 95. Ben LimmerGEMConf - 5/21/2016 ember.party https://github.com/blimmer/real-time-ember-client https://github.com/blimmer/real-time-ember-server l1m5blimmer
  • 96. Ben LimmerGEMConf - 5/21/2016 ember.party thanks!
  • 97. • WebSocket: Lightweight Client-Server Communications (O’Reilly) • WebSockets: Methods for Real-Time Data Streaming (Steve Schwartz) Credits
  • 98. • pusher.com • socket.io • node ws • websocket security (heroku) Resources