Sometimes web sockets_dont_work

1,061 views

Published on

Presentation by Pawel Ledwon at the 1st XMPP / Realtime Meetup

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,061
On SlideShare
0
From Embeds
0
Number of Embeds
29
Actions
Shares
0
Downloads
8
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Sometimes web sockets_dont_work

  1. 1. SOMETIMESWEBSOCKETSDON’T WORK @pawel_ledwon
  2. 2. WE DOWEBSOCKETS …and a bit more
  3. 3. REASONS TO LOVEWebSockets persistent and bi-directional very straightforward API don’t incur too much latency
  4. 4. var websocket = new WebSocket("ws://example.com");websocket.onopen = function() { websocket.send("Howdy!");};websocket.onmessage = function(message) { console.log("BEEP", message.data);};
  5. 5. DONE!
  6. 6. NOPE!
  7. 7. WHAT’S THE ISSUE?1.ancient browsers2.corporate firewalls3.restrictive proxies ...
  8. 8. L E T ’SSOLVETHEM!
  9. 9. PRIMARY OBJECTIVES1.improve connectivity2.provide clients with best possible transport3.improve initial latency
  10. 10. SECONDARY OBJECTIVES1.keep costs reasonable2.gather some metrics3.allow experimenting
  11. 11. DESIGN
  12. 12. TRANSPORTa type ofconnection
  13. 13. DIFFERENT TRANSPORTS WebSockets Flash HTTP-based (SSL/Non-SSL)
  14. 14. TRANSPORT CLASSTransport.connect(url) ...
  15. 15. STRATEGYa recipe for findingbest workingtransport
  16. 16. STRATEGIES SHOULD BE 1.quick and effective 2.easy to understand 3.also easy to test 4.simple to modify
  17. 17. BACK TO OUR PROBLEMS 1.ancient browsers 2.corporate firewalls 3.restrictive proxies
  18. 18. ANCIENT BROWSERSeasiest problem to solve client-side checksno communication needed
  19. 19. if (WebSocketTransport.isSupported()) { return WebSocketTransport;} else if (FlashTransport.isSupported()) { return FlashTransport;} else { return HTTPTransport;}
  20. 20. TRANSPORT CLASSTransport.connect(url)Transport.isSupported()
  21. 21. BACK TO OUR PROBLEMS 1.ancient browsers 2.corporate firewalls 3.restrictive proxies
  22. 22. FLASH & FIREWALLS neeeds port 843 to be open or…waits 3s before trying original port … you never know what’s coming
  23. 23. FLASH IS NOT THAT BAD1.it’s basically a WebSocket2.has low message latency3.requires less infrastructure
  24. 24. DIFFERENT SOLUTIONS 1.give up and avoid Flash 2.wait and switch to HTTP 3.try Flash & HTTP in parallel 1.use first connected 2.switch to Flash
  25. 25. CONNECTING IN PARALLEL Flasha) HTTP Flashb) HTTP Flashc) HTTP
  26. 26. BACK TO OUR PROBLEMS 1.ancient browsers 2.corporate firewalls 3.restrictive proxies
  27. 27. WEBSOCKETS & PROXIES some work just fine some are too old some just hate us all
  28. 28. WEBSOCKETS & PROXIESissues with upgradingfrequent disconnectionssame issue with Flash
  29. 29. DEALING WITH IT1.handle WebSockets like Flash2.use encrypted connections3.detect broken connections4.disable transports temporarily
  30. 30. CACHING1.run full strategy on 1st attempt2.cache info about best transport3.use cache on subsequent attempts
  31. 31. STRATEGIES - RECAPdetect browser features cache transport info connect in parallel detect disconnections retry attempts disable transports support delays support timeouts
  32. 32. IMPLEMENTATION
  33. 33. SINGLE RESPONSIBILITY PRINCIPLE one decision per strategy simple interface lots different types
  34. 34. var runner = strategy.connect(function(error, connection) { if (error) { console.log(":("); } else { // we can even get multiple connections console.log("We’ve got a connection!"); }});// ok, it has been long enough, I’m giving uprunner.abort();
  35. 35. COMPOSING STRATEGIES strategies form trees they can use other strategiesdecisions are still simple and local
  36. 36. STRATEGIES1.transport provides transport to strategy adapter2.if runs a test and choses another strategy3.best connected runs in parallel to find best strategy4.sequential runs strategies sequentially5.delayed runs a strategy with a delay6.cached stores and fetches cached transport info7.first connected terminates a strategy on first connection
  37. 37. transport ws
  38. 38. sequentialtransport ws
  39. 39. sequential sequentialtransport transport ws sockjs
  40. 40. delayedsequential sequentialtransport transport ws sockjs
  41. 41. best connected delayedsequential sequentialtransport transport ws sockjs
  42. 42. if ws.isSupported() true best connected delayedsequential sequentialtransport transport ws sockjs
  43. 43. if ws.isSupported() true false best connected sequential delayed transportsequential sequential sockjstransport transport ws sockjs
  44. 44. cached if ws.isSupported() true false best connected sequential delayed transportsequential sequential sockjstransport transport ws sockjs
  45. 45. REPRESENTATION
  46. 46. REPRESENTATION SHOULD BE1.understandable by humans2.easy to read in JavaScript3.possible to send to clients4.simple to modify and test
  47. 47. SENDING JS IS NOT AN OPTION 1.it’s dangerous 2.too expressive 3.difficult to test
  48. 48. JSON1.has no side-effects2.widely supported3.known by everyone
  49. 49. JSONneeds an interpreter
  50. 50. DESIRABLE FEATURES1.ability to define variables2.passing by reference3.predictable execution4.limited expressiveness
  51. 51. [ [":def", "ws_options", { hostUnencrypted: Pusher.host + ":" + Pusher.ws_port, hostEncrypted: Pusher.host + ":" + Pusher.wss_port, lives: 2 }], [":def", "sockjs_options", { hostUnencrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_http_port, hostEncrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_https_port }], [":def", "timeouts", { loop: true, timeout: 15000, timeoutLimit: 60000 }], [":def_transport", "ws", "ws", 3, ":ws_options"], [":def_transport", "sockjs", "sockjs", 1, ":sockjs_options"], [":def", "ws_loop", [":sequential", ":timeouts", ":ws"]], [":def", "sockjs_loop", [":sequential", ":timeouts", ":sockjs"]], [":def", "strategy", [":cached", 1800000, [":if", [":is_supported", ":ws"], [ ":best_connected_ever", ":ws_loop", [":delayed", 2000, [":sockjs_loop"]] ], [ ":sockjs_loop" ] ]] ] ]]
  52. 52. [ [":def", "ws_options", { hostUnencrypted: Pusher.host + ":" + Pusher.ws_port, hostEncrypted: Pusher.host + ":" + Pusher.wss_port, lives: 2 }], [":def", "sockjs_options", { hostUnencrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_http_port, hostEncrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_https_port }], [":def", "timeouts", { loop: true, timeout: 15000, timeoutLimit: 60000 }], [":def_transport", "ws", "ws", 3, ":ws_options"], [":def_transport", "sockjs", "sockjs", 1, ":sockjs_options"], [":def", "ws_loop", [":sequential", ":timeouts", ":ws"]], [":def", "sockjs_loop", [":sequential", ":timeouts", ":sockjs"]], [":def", "strategy", [":cached", 1800000, [":if", [":is_supported", ":ws"], [ ":best_connected_ever", ":ws_loop", [":delayed", 2000, [":sockjs_loop"]] ], [ ":sockjs_loop" ] ]] ] ]]
  53. 53. [ [":def", "ws_options", { hostUnencrypted: Pusher.host + ":" + Pusher.ws_port, hostEncrypted: Pusher.host + ":" + Pusher.wss_port, lives: 2 }], [":def", "sockjs_options", { hostUnencrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_http_port, hostEncrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_https_port }], [":def", "timeouts", { loop: true, timeout: 15000, timeoutLimit: 60000 }], [":def_transport", "ws", "ws", 3, ":ws_options"], [":def_transport", "sockjs", "sockjs", 1, ":sockjs_options"], [":def", "ws_loop", [":sequential", ":timeouts", ":ws"]], [":def", "sockjs_loop", [":sequential", ":timeouts", ":sockjs"]], [":def", "strategy", [":cached", 1800000, [":if", [":is_supported", ":ws"], [ ":best_connected_ever", ":ws_loop", [":delayed", 2000, [":sockjs_loop"]] ], [ ":sockjs_loop" ] ]] ] ]]
  54. 54. [ [":def", "ws_options", { hostUnencrypted: Pusher.host + ":" + Pusher.ws_port, hostEncrypted: Pusher.host + ":" + Pusher.wss_port, lives: 2 }], [":def", "sockjs_options", { hostUnencrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_http_port, hostEncrypted: Pusher.sockjs_host + ":" + Pusher.sockjs_https_port }], [":def", "timeouts", { loop: true, timeout: 15000, timeoutLimit: 60000 }], [":def_transport", "ws", "ws", 3, ":ws_options"], [":def_transport", "sockjs", "sockjs", 1, ":sockjs_options"], [":def", "ws_loop", [":sequential", ":timeouts", ":ws"]], [":def", "sockjs_loop", [":sequential", ":timeouts", ":sockjs"]], [":def", "strategy", [":cached", 1800000, [":if", [":is_supported", ":ws"], [ ":best_connected_ever", ":ws_loop", [":delayed", 2000, [":sockjs_loop"]] ], [ ":sockjs_loop" ] ]] ] ]]
  55. 55. METRICS
  56. 56. WE COLLECT1.transport state changes2.browser name, features3.some strategy actions
  57. 57. BETA DEPLOYMENTS1.deploy a new feature2.wait for enough data3.evaluate all metrics4.keep or revert changes
  58. 58. THANKS! @pawel_ledwon

×