Successfully reported this slideshow.

Building Realtime Apps with Ember.js and WebSockets

4

Share

1 of 98
1 of 98

Building Realtime Apps with Ember.js and WebSockets

4

Share

Download to read offline

This talk discusses how AJAX differs from WebSockets and how the technology can be used to implement rich real-time experiences. It also produces a live demo using EmberJS.

This talk discusses how AJAX differs from WebSockets and how the technology can be used to implement rich real-time experiences. It also produces a live demo using EmberJS.

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Building Realtime Apps with Ember.js and WebSockets

  1. 1. Building Real-Time Apps with EmberJS & WebSockets
  2. 2. Ben LimmerGEMConf - 5/21/2016 ember.party blimmer
  3. 3. Ben LimmerEmberJS Meetup - 2/24/2016 ember.party ♥
  4. 4. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • WebSockets vs. AJAX • Fundamentals of WebSockets • Code! • Other Considerations
  5. 5. Ben LimmerGEMConf - 5/21/2016 ember.party request response request response AJAX
  6. 6. Ben LimmerGEMConf - 5/21/2016 ember.party with a lot of apps, this paradigm still works
  7. 7. Ben LimmerGEMConf - 5/21/2016 ember.party but what about real-time apps?
  8. 8. Ben LimmerGEMConf - 5/21/2016 ember.party e.g.
  9. 9. Ben LimmerGEMConf - 5/21/2016 ember.party live dashboards
  10. 10. Ben LimmerGEMConf - 5/21/2016 ember.party Source: http://www.heckyl.com/
  11. 11. Ben LimmerGEMConf - 5/21/2016 ember.party 2nd screen apps
  12. 12. Ben LimmerGEMConf - 5/21/2016 ember.party© MLB / Source: MLB.com
  13. 13. Ben LimmerGEMConf - 5/21/2016 ember.party deployment notifications
  14. 14. Ben LimmerGEMConf - 5/21/2016 ember.party Source: inbox.google.com
  15. 15. Ben LimmerGEMConf - 5/21/2016 ember.party games
  16. 16. Ben LimmerGEMConf - 5/21/2016 ember.party Source: http://browserquest.mozilla.org/img/common/promo-title.jpg
  17. 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. 18. Ben LimmerGEMConf - 5/21/2016 ember.party how do we build a real-time app?
  19. 19. Ben LimmerGEMConf - 5/21/2016 ember.party update? nope. (old way) short polling update? nope. data update? yep!
  20. 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. 21. Ben LimmerGEMConf - 5/21/2016 ember.party WebSockets handshake connection opened bi-directional communication
  22. 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. 23. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • WebSockets vs. AJAX • Fundamentals of WebSockets • Code! • Other Considerations
  24. 24. Ben LimmerGEMConf - 5/21/2016 ember.party the handshake
  25. 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. 26. events • open • message • error • close • send • close methods
  27. 27. Ben LimmerGEMConf - 5/21/2016 ember.party WebSocket.send()
  28. 28. Ben LimmerGEMConf - 5/21/2016 ember.party send(String 'foo'); send(Blob 010101); send(ArrayBuffer file);
  29. 29. Ben LimmerGEMConf - 5/21/2016 ember.party WebSocket.send(’YOLO’);
  30. 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. 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. 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. 33. Ben LimmerGEMConf - 5/21/2016 ember.party STOMP SEND destination:/queue/a hello queue a ^@
  34. 34. Ben LimmerGEMConf - 5/21/2016 ember.party subprotocols bring structure to ws
  35. 35. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • AJAX vs. WebSockets • Fundamentals of WebSockets • Code! • Other Considerations
  36. 36. Ben LimmerGEMConf - 5/21/2016 ember.party let’s build something!
  37. 37. Ben LimmerGEMConf - 5/21/2016 ember.party
  38. 38. Ben LimmerEmberJS Meetup - 2/24/2016 ember.party alice clicks bob / everyone sees
  39. 39. Ben LimmerEmberJS Meetup - 2/24/2016 ember.party bob clicks alice / everyone sees
  40. 40. Ben LimmerGEMConf - 5/21/2016 ember.party npm install ws
  41. 41. Ben LimmerGEMConf - 5/21/2016 ember.party
  42. 42. Ben LimmerGEMConf - 5/21/2016 ember.party • fast • simple WebSocket implementation • few bells and whistles npm install ws
  43. 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. 44. Ben LimmerGEMConf - 5/21/2016 ember.party waiting for socket connection…
  45. 45. Ben LimmerGEMConf - 5/21/2016 ember.party ember install ember-websockets
  46. 46. Ben LimmerGEMConf - 5/21/2016 ember.party
  47. 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. 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. 49. Ben LimmerGEMConf - 5/21/2016 ember.party
  50. 50. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol
  51. 51. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  52. 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. 53. Ben LimmerGEMConf - 5/21/2016 ember.party
  54. 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. 55. Ben LimmerGEMConf - 5/21/2016 ember.party
  56. 56. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  57. 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. 58. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  59. 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. 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. 61. Ben LimmerGEMConf - 5/21/2016 ember.party
  62. 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. 63. Ben LimmerGEMConf - 5/21/2016 ember.party
  64. 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. 65. Ben LimmerGEMConf - 5/21/2016 ember.party
  66. 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. 67. Ben LimmerGEMConf - 5/21/2016 ember.party
  68. 68. Ben LimmerGEMConf - 5/21/2016 ember.party
  69. 69. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol data events
  70. 70. Ben LimmerGEMConf - 5/21/2016 ember.party rtember-1.0 - sub-protocol { "frameType": "event", "payload": { “eventType": ... event type ..., "eventInfo": ... event info ... } }
  71. 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. 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. 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. 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. 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. 76. Ben LimmerGEMConf - 5/21/2016 ember.party beware
  77. 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. 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. 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. 80. Ben LimmerGEMConf - 5/21/2016 ember.party
  81. 81. Ben LimmerGEMConf - 5/21/2016 ember.party ember.party/gemconf
  82. 82. Ben LimmerGEMConf - 5/21/2016 ember.party
  83. 83. Ben LimmerGEMConf - 5/21/2016 ember.party Talk Roadmap • AJAX vs. WebSockets • Fundamentals of WebSockets • Code! • Other Considerations
  84. 84. Ben LimmerGEMConf - 5/21/2016 ember.party other considerations • security • websocket support (libraries) • learn from example
  85. 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. 86. Ben LimmerGEMConf - 5/21/2016 ember.party other considerations • security • websocket support (libraries) • learn from example
  87. 87. Ben LimmerGEMConf - 5/21/2016 ember.party support
  88. 88. Ben LimmerGEMConf - 5/21/2016 ember.party 9
  89. 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. 90. Ben LimmerGEMConf - 5/21/2016 ember.party
  91. 91. Ben LimmerGEMConf - 5/21/2016 ember.party pusher.com • Graceful fallback • Presence support • Authentication / Security strategies • No infrastructure required
  92. 92. Ben LimmerGEMConf - 5/21/2016 ember.party other considerations • security • websocket support (libraries) • learn from example
  93. 93. Ben LimmerGEMConf - 5/21/2016 ember.party learn by example
  94. 94. Ben LimmerGEMConf - 5/21/2016 ember.party learn by example
  95. 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. 96. Ben LimmerGEMConf - 5/21/2016 ember.party thanks!
  97. 97. • WebSocket: Lightweight Client-Server Communications (O’Reilly) • WebSockets: Methods for Real-Time Data Streaming (Steve Schwartz) Credits
  98. 98. • pusher.com • socket.io • node ws • websocket security (heroku) Resources

×