We’re using WebSocket connections to provide real-time information to the users of our PHP web application. A year ago we implemented a solution based on the WebSocket-emulation layer Socket.IO. Due to some issues we switched to SockJS few months ago. For both we’ve set up a small server in Node.js which relays the push messages from our PHP application to the WebSocket-emulation, which then pushes them to the browser:
The above drawing shows the flow of real-time information coming from an XHR-request from a browser, ending in a WebSocket message sent to one (or multiple) browsers. For queueing and relaying messages between PHP and Node.js we use Redis pub/sub (the code is on github).
It would be nice if we could do all this with just PHP. This seems to be possible with Ratchet, which uses the “stream_select()“-functionality to provide a WebSocket server. But how fast is this?
Simulating WebSocket clients
To estimate how a WebSocket server with Ratchet would perform, let’s create a simple benchmarking script: websocket-benchmark is a Node.js-script which opens connections to a WebSocket server, sends some messages to it, closes the connection and in the end shows the average elapsed time.
The following command will create 1000 WebSocket clients (running 10 concurrently) which will each send 5 messages to “echo.websocket.org”:
$ ./bin/websocket-benchmark.js -h ‘ws://echo.websocket.org/’ -n 1000 -c 10 -r 5 Starting 1000 clients doing 5 roundtrips to ‘ws://echo.websocket.org/’. Min: 806ms Mean: 884.8ms Max: 1967ms
So let’s start an echo server with SockJS in a local virtual machine running Debian squeeze. Five WebSocket-roundtrips take 34 ms, the bottleneck is the CPU:
$ ./bin/websocket-benchmark.js -h ‘ws://10.10.10.10/echo/websocket’ -n 1000 -c 10 -r 5 Starting 1000 clients doing 5 roundtrips to ‘ws://10.10.10.10/echo/websocket’. Min: 24ms Mean: 33.89ms Max: 76ms
Now that we have some reference timings, let’s set up an echo server with Ratchet . Again the CPU is limiting, but the server seems to respond fast:
$ ./bin/websocket-benchmark.js -h ‘ws://10.10.10.10′ -n 1000 -c 10 -r 5 Starting 1000 clients doing 5 roundtrips to ‘ws://10.10.10.10′. Min: 28ms Mean: 46.461ms Max: 84ms
Now let’s update PHP from 5.3 to 5.4 as
suggested by @igorwesome:
$ ./bin/websocket-benchmark.js -h ‘ws://10.10.10.10′ -n 1000 -c 10 -r 5 Starting 1000 clients doing 5 roundtrips to ‘ws://10.10.10.10′. Min: 20ms Mean: 33.945ms Max: 57ms
..let’s also install the “libevent” wrapper for PHP, so Ratchet can use that instead of “stream_select()”:
$ apt-get install libevent-dev $ pecl install channel://pecl.php.net/libevent-0.0.5 $ ./bin/websocket-benchmark.js -h ‘ws://10.10.10.10′ -n 1000 -c 10 -r 5 Starting 1000 clients doing 5 roundtrips to ‘ws://10.10.10.10′. Min: 18ms Mean: 35.715ms Max: 66ms
So the libevent seems not to help in my case.
These basic benchmarks show that Ratchet should perform similar to a SockJS/Node.js-based solution. Still they don’t cover everything that’s of interest when using it in production.
The next step for us will be to test it on real traffic. Does anyone have experience with that?
The conducted benchmark is very simple, I only measured roundtrip-delay implicitly, and concurrency was quite low. Still it should give a rough estimate. Since not all browsers implement WebSocket support, Ratchet and SockJS can fall back to different other “transports” (Longpolling, Flash socket, etc.). Depending on how many IE-users you have, these fall backs can be more important than actual WebSocket connections.