WordPress mit HHVM und PHP-FPM als Fallback
Man liest ja immer, dass HHVM im Vergleich zu PHP, Seiten um den Faktor 2-4x schneller ausliefern soll.
Nachdem HHVM nun als relativ stabil zu bezeichnen ist, inzwischen voll kompatibel zu PHP sein soll, auf einer meiner Webseiten schon PHP-FPM läuft, habe ich auf diesem Nginx-Vhost einfach mal HHVM eingerichtet um dieser These nachzugehen.
Da die Implementation von HHVM wirklich einfach ist, wollte ich die Hürde ein klein wenig höher legen, und habe PHP-FPM als Fallback – falls HHVM mal abkachelt – einfach mit eingebaut.
1. Was ist HHVM eigentlich?
HHVM (HipHop Virtual Machine) ist in Hack und PHP geschrieben, um PHP-Applikationen wie z.B. WordPress ausführen zu können. HHVM benutzt dazu u.a. einen JIT-Compiler (Just In Time).
Auch wenn das Ganze von Facebook entwickelt wurde, ist HHVM Open Source.
HHVM portiert aufgerufenen PHP-Quelltext in einen speziellen Bytecode (Facebook nennt diesen „HHBC“). Dieser Bytecode wird mit Hilfe des JIT-Kompilers in X64-Maschinensprache übersetzt.
Aus diesem Mechanismus heraus, soll HHVM seinen Performance Vorteil beziehen.
Mehr Informationen kann der offiziellen HHVM Webseite, bzw. dem sehr informativen HHVM-Blog entnommen werden.
2. Installation und Konfiguration von HHVM
HHVM kann man als Ubuntu-Paket direkt vom HHVM-Projekt installieren, bzw. in die sources.list einbinden. Damit wird stets die aktuellste, stabile Version installiert:
sudo apt-get install software-properties-common
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0x5a16e7281be7a449
sudo add-apt-repository "deb http://dl.hhvm.com/ubuntu $(lsb_release -sc) main"
sudo apt-get update
sudo apt-get install hhvm
Wie gewohnt landen die Konfigurationsdateien unter /etc/ im Ordner hhvm.
Per default läuft HHVM auf dem TCP Port 9000. Da ich HHVM auf meinem Server lokal anspreche, habe ich HHVM auf einen Unix-Socket „umgebogen“, zudem dieser im Vergleich zu einem TCP Port in der Regel ein wenig performanter ist.
So sieht meine /etc/hhvm/server.ini wie folgt aus:
; php options
pid = /var/run/hhvm/pid
; hhvm specific
hhvm.server.file_socket=/var/run/hhvm/hhvm.sock
hhvm.server.type = fastcgi
hhvm.server.default_document = index.php
hhvm.log.use_log_file = true
hhvm.log.file = /var/log/hhvm/error.log
hhvm.repo.central.path = /var/run/hhvm/hhvm.hhbc
hhvm.mysql.socket = /var/run/mysqld/mysqld.sock
hhvm.server.fix_path_info = false
hhvm.log.header = true
hhvm.log.natives_stack_trace = true
Das war es auch schon … und HHVM kann mittels „service hhvm start“ auf der Konsole gestartet werden.
3. Einbinden von HHVM in den Nginx-Vhost
Die Konfiguration / Einbindung von HHVM in einen Nginx-Vhost ist prinzipiell analog zu der Einbindung von PHP-FPM; also keine „rocket science“:
...
location ~ .(hh|php)$ {
fastcgi_intercept_errors on;
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 unix:/var/run/hhvm/hhvm.sock;
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_read_timeout 240;
}
...
Die Nginx-Konfiguration kann also neu geladen werden, natürlich erst nach einem Test der neuen Konfiguration:
nginx -t && service nginx reload
Der Test, ob der konfigurierte VHost nun auch mit HHVM läuft ist einfach:
foo@something:/etc/nginx# curl -I https://www.365-tage-fotoprojekte.de
HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: HHVM/3.10.1
Strict-Transport-Security: max-age=31536000
Front-End-Https: on
X-Frame-Options: SAMEORIGIN
Date: Wed, 02 Dec 2015 16:29:23 GMT
X-Page-Speed: 1.9.32.10-7423
Cache-Control: max-age=0, no-cache, no-store
4. PHP-FPM als HHVM Fallback
Auf vielen Seiten im Netz findet man die Aussage, dass sich HHVM ab und an ins Datenvirvana verabschiedet. Da ich keine Nginx-Fehlerseiten, sondern Content ausliefern möchte, habe ich PHP-FPM als Fallback für HHVM eingebaut. Die Nginx-Vhost Konfiguration wird dazu wie folgt ergänzt:
...
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 unix:/var/run/hhvm/hhvm.sock;
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_read_timeout 240;
}
# PHP-FPM Fallback
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;
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_read_timeout 240;
fastcgi_intercept_errors on;
}
...
Wie üblich wird die Nginx-Konfiguration neu geladen:
nginx -t && service nginx reload
Der Fallback-Test ist wieder mittels curl durchzuführen:
foo@something:/etc/nginx# service hhvm stop foo@something:/etc/nginx# curl -I https://www.365-tage-fotoprojekte.de HTTP/1.1 200 OK Server: nginx Content-Type: text/html; charset=UTF-8 Connection: keep-alive Vary: Accept-Encoding X-Powered-By: PHP/5.5.9-1ubuntu4.14 Pragma: no-cache Strict-Transport-Security: max-age=31536000 Front-End-Https: on X-Frame-Options: SAMEORIGIN Date: Wed, 02 Dec 2015 16:38:06 GMT X-Page-Speed: 1.9.32.10-7423 Cache-Control: max-age=0, no-cache, no-store, must-revalidate, post-check=0, pre-check=0 foo@something:/etc/nginx# service hhvm start foo@something:/etc/nginx# curl -I https://www.365-tage-fotoprojekte.de HTTP/1.1 200 OK Server: nginx Content-Type: text/html; charset=UTF-8 Connection: keep-alive Vary: Accept-Encoding X-Powered-By: HHVM/3.10.1 Pragma: no-cache Strict-Transport-Security: max-age=31536000 Front-End-Https: on X-Frame-Options: SAMEORIGIN Date: Wed, 02 Dec 2015 16:38:17 GMT X-Page-Speed: 1.9.32.10-7423 Cache-Control: max-age=0, no-cache, no-store
5. Ist HHVM nun schneller als PHP-FPM?
Die ernüchternde Antwort ist: Nein.
Also zumindest nicht auf meinem Server 🙂
Mittels dem Apache-Benchmark-Tool (ab) habe ich eine „Hallo Welt“ PHP-Datei mehrfach mit und ohne HHVM / PHP-FPM penetriert.
Bei 25 gleichzeitigen Usern und 5000 Requesten (ab -k -c 25 -n 50000 https://www.365-tage-fotoprojekte.de/hallo.php) bringt es HHVM im Durchschnitt auf 241.27 Requests pro Sekunde, PHP-FPM (mit aktiviertem Opcache!) auf 221.25 Requests pro Sekunde. Der „Performance-Gewinn“ von HHVM liegt auf meinem Server also im Durchschnitt bei 20 Requests pro Sekunde.
Meine Conclusio: HHVM ist schneller, aber nur geringfügig.
Trotzdem werde ich auf diesem Nginx-Vhost weiterhin auf HHVM und PHP-FPM als Fallback setzen, nicht weil die darunterlegende Webseite in einen Geschwindigkeitsrausch verfällt, nein … weil ich es kann 🙂
Interessant wird es, wenn die PHP Entwickler irgendwann eine stabile PHP7 Version releasen. Dann muss HHVM gegen PHP7 antreten. Ich werde darüber berichten.