using web sockets in your php application - truedeveloper.co · including sockets • php has a...

36
Using web sockets in your PHP application Jeff Kolesnikowicz

Upload: truongkhanh

Post on 23-Nov-2018

239 views

Category:

Documents


0 download

TRANSCRIPT

UsingwebsocketsinyourPHPapplication

JeffKolesnikowicz

Mymomtoldmetostartwithajoke…

Fwd: FW: Fw: Fwd: Fwd: Computer Cartoons

Aboutme

• Buildingwebsitessince1999,workingwithPHPsince2001

• Officemanager(cat)doesn’tgetenoughscratches

Whatarewebsockets?

• PartofHTML5• Full-duplexclient/servercommunication

• Forrealtimecommunicationbetweenclientandserver

• accessiblefromnon-browserapplications

• Builtinto(most)browsers.

Source:http://caniuse.com/#feat=websockets

WebsocketsVSAJAX • Websocketsarefull-duplex

• Client-serverrelationship• Connectionispersistentandstateful

• Authenticationisbuilt-intoAJAX

WAMP• No,notthestack• WebApplicationMessagingProtocol

• Facilitatestwotypesofmessagingpatterns:publishandsubscribe(pub/sub)and,remoteproceduralcall(RPC)

WAMP

• Aclientsubscribestoatopicandreceivesallmessagesinthetopic

• Notlimitedto1:1communication.

• Scalesfrom0toNclients• Clientcannotrequestinformationoutsideofwhatispushed

• Canbeusedasachat,updatingconfigurationbetweenservices

Pub/Sub

WAMP• Cancallcustomfunctions• 1:1communicationwiththeserver

• Canbeusedtorequestdata,directcommunicationbetweenbrowsers,games

• Servercanmakearequestofthebrowserandviceversa

RPC

Ratchet• PHPimplementationofWAMPprotocol

• Providesserversidehooksforpub/subaswellasRPC

• Providesaflashbasedclientsidepolyfill

• Providesaread-onlySymfonysessionhandler

• Andmore!• Canadian!

Thenewshiny• RatchetdoesnotsupportWAMP2

• Thruwaydoes(https://github.com/voryx/Thruway)

WAMPServerServer:

<?php use Ratchet\Server\IoServer; use Ratchet\Http\HttpServer; use Ratchet\WebSocket\WsServer; use Ratchet\Wamp\WampServer; use Ratchet\Cookbook\OpenPubSub;

require dirname(__DIR__) . '/vendor/autoload.php';

use MyApp\WampDemo;

$server = IoServer::factory( new HttpServer( new WsServer( new WampServer( new WampDemo ) ) ) , 8080

); $server->run();

class WampDemo implements WampServerInterface { protected $clients;

public function __construct() {

$this->clients = new \SplObjectStorage; }

public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {

echo "Calling $topic"; }

public function onSubscribe(ConnectionInterface $conn, $topic) { echo "Subscribing to $topic";

}

public function onUnSubscribe(ConnectionInterface $conn, $topic) { echo "Unsubscribing to $topic";

}

public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) { echo "Publishing to $topic";

}

public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); echo "Connected\n";

}

public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn);

echo "Closed\n"; }

public function onError(ConnectionInterface $conn, \Exception $e) { echo "An error has occurred: {$e->getMessage()}\n";

$conn->close(); }

}

Clientside<script>

var connection = new autobahn.Connection({

url: “ws://127.0.0.1:8080”,

realm: "demo"

});

connection.onopen = function (session, details) {

console.log("Connection established!", session, details);

session.call(“test");

session.subscribe("test.topic");

session.publish("test.topic");

};

connection.onclose = function (reason, details) {

console.log("Connection lost: " + reason);

}

connection.onmessage = function(e) {

console.log(e.data);

};

connection.open();

</script>

Demo

Howweusedwebsockets

• Applicationbuiltbyexternalvendor

• eachAJAXrequestveryslow-1000ms+

• Dataintegritywaspoor:APP.Case.Score=100;

• Duplicationoffunctionalitybetweenclientandserverside

• Gamewasattachedtoalargelegacyapplication

Theproblem

Howweusedwebsockets

• Movelogicsomewherethatcan’tbeaccessedbyauser

• Speedapplicationup• Needstobeextensible• Couldnotbeentirelyrewritten

• Thebusinesswantsitdoneyesterday

Whatweneededtodo

Howweusedwebsockets

• WebsocketsarefasterthanAJAX

• Websocketsarepersistent• Scoringlogicnowserverside

• Gameisnoweventdriven• Lostdataislesslikely

Thesolution

HowweusedwebsocketsTheapplication

Notsofast…• PHPisnotgreatatrunning24/7

• CrashyMcCrashface• Firewalls&Proxies

akaThesearethepitfallsI’veknown

CachingCrashes• Getourhandsdirtywithcoredumps

• Crashingwhencallingredit_sock_write

• Connectiontocachingserverwasgettingdisconnected

ulimitgottabekiddingme

PHP Fatal error: Uncaught exception ... Too many open files

• EverythinginUNIXisafile-includingsockets

• PHPhasadefaultlimitof1024openfiles

jeff@devlocal:~$ ulimit -n 65535

ulimitgottabekiddingme

• OSgavePHPafilehandlethatwashigherthanexpected,socrashed

• Atheoreticallimitof1024connectionsperserver(probablylower)

• lowernumberofopenfiles• Createnewuserandedit/etc/security/limits.conf

websocket-user hard nofile 1024 websocket-user soft nofile 1024

Problembyproxy

• Morerestrictivefirewallsmayneedtowhitelistwebsocketservers

• Usefulfordiagnosis:http://www.websocket.org/echo.html

• Matchprotocolofwebpageandwebsocket(IE)

• Runwsstopassthroughproxyservers

• Manyfirewallsblockportsabove1024

Balancingdaemons

• Useupstarttorunwebsocketserver

• Restartdaily• Buildinareconnecttohelpautomaticallyreconnectusers

Abalancingact• Rackspaceloadbalancersdon’tsupportwebsockets

• Amazon’sELBdoes(withsomework)

• HAProxydoes• ApacheandNginxsupportloadbalancingandroutingthroughaURL

Nginx

• WeloadbalancethroughNginx(willswitchtoHAProxy)

• AbletoredirectwebsocketstoaURL

• Routedthroughport443

Nginx/websocket/

(port 80)

Websocket server

(port 8000)

Websocket server

(port 8000)

Websocket server

(port 8000)

Nginxconfiguration

http {

map $http_upgrade $connection_upgrade {

default upgrade;

'' close;

}

upstream websocket {

least_conn;

server 192.168.100.10:8000;

server 192.168.100.11:8000;

server 192.168.100.12:8000;

}

server {

listen 8000;

location /websocket/ {

proxy_pass http://websocket;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection $connection_upgrade;

}

Monitoring• Loadbalancinghelps,butrequiresanumberoffailuresbeforedown

• Addinganewpieceofinfrastructure-monitorit!

• Pingdomdon’tsupportwebsocketmonitoring

• Newrelicsupportsprocessmonitoring

Monitoringprivate function pingServer($server, $port, $path) { $loop = React\EventLoop\Factory::create(); App::import('Lib', 'Simucase.Client');

$client = new Client($loop, $server, $port, $path); $response = null;

$params = [json_encode([])];

$client->setOnWelcomeCallback(function (Client $conn, $data) use (&$response, $loop, $params) { $conn->call('helloWorld', $params, function ($data) use (&$response, $loop, $conn) { $response = $data; $loop->stop(); }); });

$loop->run(); return $response; }

public function pingServers() {

$reports = [];

foreach ($this->servers as $server) {

try {

$report = $this->pingServer($server, $this->port, $this->path);

}

catch (Exception $e) {

$report = "$server is down";

$value = ['server' => $server];

AppStat::increment('webapp.server.down', $value);

}

$reports[$server] = $report;

}

return $reports;

}

Provideuserswithfeedback

Buildacompatibilitypageforusers

http://www.speechpathology.com/simucase/case-studies/browser-test

InSummary• FasterandmorerobustthanAJAX

• ThinklongandhardbeforeusingPHP

• Addingapieceoffussyinfrastructure

• Knowyouraudience• Informyouraudience

Thankyou!Questions?Slidesavailableat

http://truedeveloper.co/blog/websockets/

Pleaseratemytalk:

https://legacy.joind.in/19720

Samplecodeavailableathttps://github.com/jeffkolez/ratchet-websocket-demo

I’montwitter:@jkolezEmail:[email protected]