Slide 1

Slide 1 text

@J7mbo

Slide 2

Slide 2 text

Two words about myself @J7mbo James Mallison

Slide 3

Slide 3 text

@J7mbo

Slide 4

Slide 4 text

Websockets and Torrents A match made with PHP! (and not nodeJS) @J7mbo • The story of my interest in websockets • Implementing the technology for arbitrary (torrent) data • Problems I encountered and solved • A Demo!! (this completely failed at PHPNW) • What I learned

Slide 5

Slide 5 text

Legit speeds @J7mbo

Slide 6

Slide 6 text

@J7mbo

Slide 7

Slide 7 text

Transmission - Worst Torrent GUI Ever @J7mbo

Slide 8

Slide 8 text

(Can I get this in Yaml?) @J7mbo transmission-remote 127.0.0.1:9091 --debug -l

Slide 9

Slide 9 text

• Browser makes AJAX request • Server performs CLI call • Server returns Json Response AJAX Request Browser Server Command line Json Response Browser Server AJAX to Server @J7mbo

Slide 10

Slide 10 text

@J7mbo

Slide 11

Slide 11 text

@J7mbo

Slide 12

Slide 12 text

• Browser makes AJAX request • Server SSHs to VPS over TOR • SSH over TOR using Torify • VPS performs CLI call • VPS returns Json Response • Server returns Json Response Browser My Server SSH to VPS Json Response Browser My Server Command line AJAX Request Ajax with SSH to VPS @J7mbo TOR (torify)

Slide 13

Slide 13 text

@J7mbo

Slide 14

Slide 14 text

@J7mbo

Slide 15

Slide 15 text

• Browser sends ws:// or wss:// • Server responds with HTTP/ 1.1 Switching Protocols • Open and persistent connection • Connection can be closed by either server or client at any time. Websockets @J7mbo

Slide 16

Slide 16 text

Request to Server Response to Client END Http VS Websockets • Send a request • HALF DUPLEX • Wait for a response • Rinse, repeat Http Event Loop WS Connection Started Send response Send response Send another response Receive message Websockets • Initialise connection • FULL DUPLEX • Send / receive at any time • Close connection @J7mbo

Slide 17

Slide 17 text

Duplex wtf? @J7mbo Half Duplex In a conversation, only one person can talk at once The other person has to listen and wait to give a response Full Duplex In a conversation, everyone’s able to talk and listen at once Everyone is able to understand what each other is saying (Like PHP Internals) (Not like PHP Internals)

Slide 18

Slide 18 text

@J7mbo Event Loop

Slide 19

Slide 19 text

• Not just while(){} loops • Use interrupt driven I/O instead of polling • IDI/O flag is set on CPU, process suspended and buffer filled • Flag changes, callback executed (Still requires flag checks, not magic) • Useful for IPC • PHP’s PCNTL similar to this (signal based) @J7mbo Event Loops

Slide 20

Slide 20 text

PCNTL Extension @J7mbo

Slide 21

Slide 21 text

@J7mbo Ratchet

Slide 22

Slide 22 text

• Create “Event Handler” class that implements Ratchet’s interface that has methods “onSubscribe()”, “onOpen()” etc @J7mbo Using Ratchet @J7mbo • Tell Ratchet that your class is the thing that will handle events and is ready to be run() • In development, run terminal, type php index.php, hit enter and leave the event loop (and event handler class) terminal window open, running, and ready to handle events

Slide 23

Slide 23 text

Using Ratchet (Code) @J7mbo The above code allows: Javascript to call .subscribe() over websockets OurEventHandlerClass::subscribe() to be executed (by event loop)

Slide 24

Slide 24 text

Implementation Time @J7mbo @J7mbo WAMP Spec (Methods you have to implement in PHP) JavaScript library calls to make events happen

Slide 25

Slide 25 text

The Web Application Messaging Protocol @J7mbo WAMP Events

Slide 26

Slide 26 text

onOpen() @J7mbo

Slide 27

Slide 27 text

onOpen() @J7mbo

Slide 28

Slide 28 text

onSubscribe() @J7mbo

Slide 29

Slide 29 text

onSubscribe() @J7mbo

Slide 30

Slide 30 text

Websocket Event Handler + Loop HTTP or Websocket? ProxyPass Http Ws Apache + PHP Client makes a request Enable mod_proxy • ProxyPass /wss/ wss://127.0.0.1:9000/ @J7mbo

Slide 31

Slide 31 text

Event Loop .subscribe(func, topic) 1 onSubscribe($conn, $topic) • store connection in array • start timer for connection • store timer against connection 2 High level functionality + events Timer hit • Get torrent data via CLI • This takes ages… … … 3 ‘Broadcast’ data back to topic • $topic->broadcast($data) 4 Client calls .unsubscribe(func, topic) • Event handler onUnsubscribe() called • Stop timer for connection • Remove from internal connections array 5 @J7mbo

Slide 32

Slide 32 text

Event Loop Problem 1 - Topic ‘Broadcast’ data back to topic • $topic->broadcast($data) 4 Option 1: $topic = ‘torrents’ • - Not on a per-user or per-connection basis • - Everyone subscribed to ‘torrents’ gets the data pushed to them • - Open same page, data gets sent to both pages twice Option 2: $topic = { ‘topic’ => ‘torrents’, ‘userId’ => 245 } • Every subscription is now unique per user id • - Same user can open same page and start getting duplicate data like before @J7mbo

Slide 33

Slide 33 text

Problem 2: Authentication and Duplicate data The Problem • Pass user id from PHP to JS variable • Subscribe to torrents with topic containing user id • Client could have just changed user id JS variable before subscribing One Solution • Create unique token on each standard HTTP request in your application • Use PHP 7’s CSPRNG rather than open_ssl_random_pseudo_bytes() • Pass token with user id in topic and check against database • Recreate token in database so that a second check will fail $topic = { ‘topic’ => ‘torrents’, ‘userId’ => 245, ‘token’ => ‘385e33f741’ // bin2hex(random_bytes(5)) in PHP 7.1 }; @J7mbo

Slide 34

Slide 34 text

Problem 3: Blocking!! The Problem • Whilst command line call is being made (5 seconds), nothing else can happen • No .subscribe ——> onSubscribe() : Nobody else can do anything • This is blocking! Same goes for authentication (checking database) A few solutions • Use another process to do the work - on the same machine • Use a job queue - requires a way to get data back into event loop once done • Requires good architecture decisions for low coupling + high cohesion for event naming, job naming, whether worker knows who’s job it is etc @J7mbo

Slide 35

Slide 35 text

Blocking - Option 1 - Child Process The Result • Event loop can continue, fine for small projects or just as a proof of concept • Process is only on one machine - not scalable @J7mbo

Slide 36

Slide 36 text

Blocking - Option 2 - Job Queue (and ZMQ) In onSubscribe() - add to queue ZMQ listening - When setting up event handler @J7mbo

Slide 37

Slide 37 text

Blocking - Option 2 - Job Queue (and ZMQ) Worker sends data over :5555 when done to onZmqResponse() @J7mbo

Slide 38

Slide 38 text

Event Loop .subscribe(func, topic) 1 High level architecture onSubscribe($conn, $topic) • store connection in array • add job to queue (fire and forget) 2 Queue Workers pick up job • Perform job • Send result to :5555 3 Workers Anything sent to port 5555 goes to onZmqRequest() Broadcasted to client 4 • Event loop is just an intermediary for data retrieval and sending • Nobody is blocked from registering their interest in receiving data • Workers can be in Java, C*, Erlang, Python, Ruby, JS etc @J7mbo

Slide 39

Slide 39 text

Demo? (Last time this failed completely) @J7mbo

Slide 40

Slide 40 text

What about scaling? (One way without a load balancer) @J7mbo Client API DB / Redis Workers Queue Call API Event Loop Event Loop Event Loop Call chosen ws:// server Round Robin? ZMQ Scalable Scalable Broadcast data to client

Slide 41

Slide 41 text

But we’re still polling on the server for torrent data!! (Obviously) @J7mbo For ‘realtime’ data, you have to program a ‘daemon’, which requires (yet again) a different mindset Monitor CPU Usage example: The crap way • Python script runs top • Parses output • SUPER inefficient The proper way • C++ Binary • Queries HOST-RESOURCES MIB • Sleeps on pthread_mutex in-between • (Involves threading or something?)

Slide 42

Slide 42 text

• Using an event loop != non-blocking or asynchronous • Easiest way to not block, use threads, processes or an external library • All connections between client / ws server over SSH with TOR • Could accomplish with NodeJS (but didn’t want to sell my soul) • Could have just made RPC calls to the server anyway instead of cmd-line (I found this out right at the end of it all) • Didn’t have to broadcast on a $topic, as $connection had methods of sending data to specific connections • Server-side real-time requires a daemon, threads, clever stuff What I learned @J7mbo

Slide 43

Slide 43 text

• Event loops and reactors in PHP: React, amphp • Websocket server-side libraries: ratchet, aerys • WAMP-specced client-side libraries: Autobahn.js, AngularWAMP, Minion • Event-loop underlying libs: LibEv, LibEvent, LibUV (windows) Your Choices @J7mbo

Slide 44

Slide 44 text

Where YOU can go next @J7mbo • Get a simple example working (slides up) • Use processes first to offload work • Get ZMQ working and use terminal to send something into your event loop! • Watch Justin Carmony’s talk from PHPNW15 on job queues and writing them in a clean and methodical OO way, and put that knowledge together with the stuff from my talk:
 http://goo.gl/7TRnOo • Have fun and be awesome because you now know a simple architecture for using websockets with PHP (and not nodejs)

Slide 45

Slide 45 text

• @j7mbo (PHP6 jokes, php, oop, software engineering) • PHPNW15: Justin Carmony - Scaling & Managing Asynchronous Workers (and staying sane!) - Watch this for queues etc - it’s an awesome talk! Thanks @J7mbo https://joind.in/talk/14c67

Slide 46

Slide 46 text

DEMO (if it works)