HHVM can really speed up your PHP-based web site. Most reports are somewhere in the range of 2–4x faster. Unfortunately, HHVM isn’t very stable and will suddenly die, just of the blue, from time to another. Fortunately, if you’re running Nginx it’s really easy to set up PHP-FPM as a fallback.
Configure HHVM and PHP-FPM
First, make sure you have a web site that works with PHP-FPM, then replace it with HHVM and make sure it runs fine.
[bjornad]
My PHP config looks like this:
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SERVER_NAME $host;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
And my HHVM config looks like this:
location ~ \.(hh|php)$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_keep_conn on;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SERVER_NAME $host;
fastcgi_pass 127.0.0.1:9000;
}
Yes, they are pretty much alike, and for absolutely no obvious reason, I run HHVM over a port and PHP-FPM through a socket. There are religious wars on the Internet on what’s best. I take no stand.
The important thing is that you don’t run HHVM and PHP-FPM over the same port or socket, since they both have to run at the same time.
Make sure your website runs fine when either of these configs are active (not both at the same time).
Use both configs – with PHP-FPM as fallback
Next we’re going to merge the two configs, into one config where HHVM is the one that will primarily handle .php-files. We will intercept the errors from HHVM, and the PHP-FPM config will be used as a fallback “location” if the error code is 502 (“Bad Gateway”), which is what is returned when HHVM falls on its face.
location ~ \.(hh|php)$ {
fastcgi_intercept_errors on;
error_page 502 = @fallback;
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_keep_conn on;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SERVER_NAME $host;
fastcgi_pass 127.0.0.1:9000;
}
location @fallback {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SERVER_NAME $host;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
Test the fallback
Make sure HHVM, PHP-FPM and Nginx is running (remember to reload Nginx after changing the config), open a terminal window and execute (replace the URL with your own):
$ curl -I https://example.com
If you haven’t removed the header X-Powered-By, you should see this line in your output:
X-Powered-By: HHVM/3.4.0
Now stop HHVM on the server:
$ service hhvm stop
The above curl command should now give you something like:
X-Powered-By: PHP/5.5.9-1ubuntu4.5
So whenever HHVM goes AWOL, the request will be passed on to PHP-FPM who will serve as backup.
Now we know that PHP-FPM will function as a fallback if HHVM dies. To make sure that PHP-FPM will function as a fallback if HHVM returns a “502 Bad Gateway”, you can use a PHP script like this one:
https://gist.github.com/bjornjohansen/735df6e1f49e3a5877b1
Save it as fallback-test.php
in your document root and test it with curl:
$ curl -i https://example.com/fallback.php
You should see the PHP-FPM X-Powered-By header and “PHP Fallback” as the body content.
Restart HHVM automatically
Now we have a mechanism in place that will fill in for HHVM. But since we have better things to do than check what service is serving our visitors, we’ll make sure HHVM is restarted automatically when it goes away.
ps-watcher is a tool that watches which processes are running and executes an action on defined circumstances. Let’s install it:
$ apt-get install ps-watcher
Edit /etc/ps-watcher.conf (you might have to create it) and add the following lines:
[hhvm]
occurs = none
action = service hhvm restart
Enable ps-watcher to start:
$ sed -i -e 's/# startup=1/startup=1/g' /etc/default/ps-watcher
By default ps-watcher will run every 5 minutes (or whatever is set in your /etc/default/ps-watcher – which on Ubuntu by default is set to 150 seconds). According to our config, the action service hhvm restart
will be run if the regex hhvm
cause no matches in the ps
command output.
Start ps-watcher:
$ service ps-watcher start
If you now kill HHVM manually, you should see it back up before 150 seconds have passed by.
There, that’s it! Now you have a stable server with HHVM.