Websockets talk at Rubyconf Uruguay 2010

4,325 views

Published on

Published in: Technology, Education

Websockets talk at Rubyconf Uruguay 2010

  1. 1. WebSockets in Ruby (and things you can do with them) Ismael Celis @ismasan github.com/ismasan new bamboo new-bamboo.co.uk pusherapp.com
  2. 2. Polling Long-polling Web socket websockets.org/about.html HTML5 Web sockets
  3. 3. Use cases •Chat (lame) •Stocks (lame but challenging) •Games •Presence •Collaboration •Real-time notifications
  4. 4. WebSockets DOM API <script type="text/javascript" charset="utf-8"> // Socket object var socket = new WebSocket('ws://some.host.com'); // Callbacks socket.onopen = function (evt) { alert('Socket connected: ' + evt.data) }; // Incoming server message socket.onmessage = function (evt) { alert( evt.data ) }; socket.onclose = function (evt) { alert('Connection terminated') // reconnect, etc. }; </script>
  5. 5. WebSockets handshake Request Response GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Sec-WebSocket-Protocol: sample Upgrade: WebSocket Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 Origin: http://example.com ^n:ds[4U HTTP/1.1 101 WebSocket Protocol Handshake Upgrade: WebSocket Connection: Upgrade Sec-WebSocket-Origin: http://example.com Sec-WebSocket-Location: ws://example.com/demo Sec-WebSocket-Protocol: sample 8jKS'y:G*Co,Wxa- (draft 76)
  6. 6. "x00Hello Worldxff" Messages // Incoming server message socket.onmessage = function (evt) { alert( evt.data ) }; // Send message to server socket.send("Hello server, this is a new client");
  7. 7. (Ruby) WebSockets server •Speak WebSockets (handshake) •Keep connections alive •Performant! •Scalable •Threads? Evented?
  8. 8. EventMachine rubyeventmachine.com require 'eventmachine' module EchoServer def receive_data(data) send_data data end end EventMachine::run { EventMachine::start_server 'localhost', 8080, EchoServer puts 'running echo server on 8080' }
  9. 9. EM-WebSocket github.com/igrigorik/em-websocket EventMachine.run { EventMachine::WebSocket.start(:host=>'0.0.0.0',:port=>8080) do |socket| socket.onopen { # publish message to the client socket.send 'Websocket connection open' } socket.onmessage {|msg| # echo message to client socket.send "Received message: #{msg}" } end }
  10. 10. Multicast - subscribers ... @channel = Channel.new ... socket.onopen { @channel.subscribe socket } socket.onmessage { |msg| @channel.send_message msg } socket.onclose { @channel.unsubscribe socket }
  11. 11. Multicast - subscribers # Server socket.onopen { @channel.subscribe socket } class Channel def initialize @sockets = [] end def subscribe( socket ) @sockets << socket end ... end
  12. 12. Multicast - channel class Channel ... def subscribe( socket ) @sockets << socket end def send_message( msg ) @sockets.each do |socket| socket.send msg end end def unsubscribe( socket ) @sockets.delete socket end end
  13. 13. Multicast - example var socket = new WebSocket('ws://localhost:8080'); socket.onmessage = function( evt ) { $('<li>') .text(evt.data) .appendTo('#messages'); } <ul id="messages"> </ul> HTML Javascript
  14. 14. Multicast - example github.com/ismasan/websockets_examples
  15. 15. Pick your protocol STOMP CONNECT login: <username> passcode: <passcode> <message from=”john@server.com/ruby” to=”jane@server.com/ruby”> <body>Hey Jane!</body> </message> XMPP Your own (JSON?)
  16. 16. Custom message format JSON
  17. 17. Custom JSON format { “event”: “user_connected”, { “name” : “Ismael”, “total_users” : 10 } } Event name Custom data “data”:
  18. 18. Custom JSON format { “event”: “user_message”, { “message” : “Hello!”, “date” : “23 2010 21:12:28” } } “data”:
  19. 19. Javascript wrapper var socket = new FancyWebSocket('ws://localhost:8080'); ... socket.bind( 'user_connected', function (user_data) { // Add user to screen $('#connected_users').append('<li>' + user_data.name + '</li>'); }); socket.bind( 'user_message', function (msg_data) { // Add message to screen $('#messages').append('<li>' + msg_data.message + '</li>'); });
  20. 20. // Broadcast message - jQuery example $('form#user_input').submit(function () { var msg = $(this).find('input[name=message]').val(); socket.send( 'user_message', {name: 'Ismael', message: msg} ); return false; }); Javascript wrapper { “event” : “user_message”, { “name” : “Ismael”, “message” : “hello!” } } “data” :
  21. 21. Implementation gist.github.com/299789 var FancyWebSocket = function(url){ var conn = new WebSocket(url); var callbacks = {}; this.bind = function(event_name, callback){ callbacks[event_name] = callbacks[event_name] || []; callbacks[event_name].push(callback); }; this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_data}); conn.send( payload ); }; // dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) }; conn.onclose = function(){dispatch('close',null)} conn.onopen = function(){dispatch('open',null)} var dispatch = function(event_name, message){ var chain = callbacks[event_name]; if(typeof chain == 'undefined') return; // no callbacks for this event for(var i = 0; i < chain.length; i++){ chain[i]( message ) } } };
  22. 22. Implementation gist.github.com/299789 var FancyWebSocket = function(url){ var conn = new WebSocket(url); var callbacks = {}; this.bind = function(event_name, callback){ callbacks[event_name] = callbacks[event_name] || []; callbacks[event_name].push(callback); }; this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_data}); conn.send( payload ); };
  23. 23. Ismael Celis Implementation gist.github.com/299789 var FancyWebSocket = function(url){ var conn = new WebSocket(url); var callbacks = {}; this.bind = function(event_name, callback){ callbacks[event_name] = callbacks[event_name] || []; callbacks[event_name].push(callback); }; this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_d conn.send( payload ); }; // dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data)
  24. 24. // dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) }; conn.onclose = function(){dispatch('close',null)} conn.onopen = function(){dispatch('open',null)} var dispatch = function(event_name, message){ var chain = callbacks[event_name]; if(typeof chain == 'undefined') return; // no callbacks for this event for(var i = 0; i < chain.length; i++){ chain[i]( message ) } Implementation WebSocket gist.github.com/299789
  25. 25. var FancyWebSocket = function(url){ // dispatch to the right handlers conn.onmessage = function(evt){ var json = JSON.parse(evt.data) dispatch(json.event, json.data) }; conn.onclose = function(){dispatch('close',null)} conn.onopen = function(){dispatch('open',null)} var dispatch = function(event_name, message){ var chain = callbacks[event_name]; // no callbacks for this event if(typeof chain == 'undefined') return; for(var i = 0; i < chain.length; i++){ chain[i]( message ) } } }; Implementation gist.github.com/299789
  26. 26. socket.send( 'user_message', {name: 'Ismael', message: msg} ); Implementation gist.github.com/299789 this.send = function(event_name, event_data){ var payload = JSON.stringify({event:event_name, data: event_data}); conn.send( payload ); // <= send JSON data to socket server return this; };
  27. 27. Multicast - JSON data github.com/ismasan/websockets_examples
  28. 28. Multicast - JSON data $('#canvas').mousedown(function () { drawing = true; }) .mouseup(function () { drawing = false; }) .mousemove(function (evt) { if(drawing) { var point = [evt.pageX, evt.pageY]; socket.send('mousemove', point); } });
  29. 29. Multicast - JSON data var ctx = document.getElementById ('canvas').getContext('2d'); ctx.lineWidth = 1; ctx.strokeStyle = '#ffffff'; ctx.beginPath(); ctx.moveTo(0, 0); // Listen to other user's moves socket.bind('mousemove', function (point) { ctx.lineTo(point[0],point[1]); ctx.stroke(); });
  30. 30. Activity dashboard
  31. 31. Rails Rumble dashboard
  32. 32. Scaling ?
  33. 33. pusherapp.com
  34. 34. pusherapp.com Your app REST Browsers Websockets Existing HTTP
  35. 35. pusherapp.com
  36. 36. pusherapp.com <script src="http://js.pusherapp.com/1.6/pusher.min.js"></script> <script type="text/javascript" charset="utf-8"> var socket = new Pusher('293d8ae77496e1fc053b'); </script>
  37. 37. pusherapp.com var socket = new Pusher('293d8ae77496e1fc053b'); // Subscribe to channels var channel = socket.subscribe( 'test' ); channel.bind( 'new_message', function (data) { alert( data.message ); })
  38. 38. pusherapp.com // Subscribe to PRESENCE channel var chat = socket.subscribe( 'presence-chat' ); // Listen to new members chat.bind( 'pusher:member_added', function (member) { alert( member.user_data.name ); })
  39. 39. require 'pusher' require 'sinatra' post '/messages' do message = Message.create(params[:message]) Pusher['presence-chat'].trigger(:new_message, message.attributes) redirect '/messages' end pusherapp.com $ gem install pusher
  40. 40. pusherapp.com github.com/ lifo/cramp ismasan/websockets_examples newbamboo/rumbledash blog.new-bamboo.co.uk blog.pusherapp.com

×