I’ve had the opportunity to try a variety of different server configurations but never really got around to trying HHVM with Magento until recently. I thought I would share a detailed walkthrough of configuring a single instance Magento server running Nginx + Fast CGI + HHVM / PHP-FPM + Redis + Percona. For the purpose of this blog post I’m assuming you are using Fedora, CentOS, or in my case RHEL 6.5.
NOTE: For an updated version visit: https://gist.github.com/tegansnyder/96d1be1dd65852d3e576
Install the EPEL, Webtatic, and REMI repos
rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
rpm -Uvh http://mirror.webtatic.com/yum/el6/latest.rpm
Install PHP 5.5.18
yum -y install php55w php55w-opcache php55w-devel php55w-mcrypt php55w-gd php55w-mbstring php55w-mysql php55w-pdo php55w-soap php55w-xmlrpc php55w-xml php55w-pdo php55w-mysqli libwebp
Install Percona
Note you may have existing mysql packages installed in your distro. If you do you will need to remove them prior to installing Percona. You can check by issuing:
rpm -qa | grep -i mysql
For instance on my server I needed to remove the following:
yum remove mysql
yum remove mysql-libs
yum remove compat-mysql51
Setup the Percona Repo
Open a VI editor to the following file.
vi /etc/yum.repos.d/Percona.repo
Add the following:
[percona]
name = CentOS $releasever - Percona
baseurl=http://repo.percona.com/centos/$releasever/os/$basearch/
enabled = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-percona
gpgcheck = 1
Grab the Percona GPG key
wget http://www.percona.com/downloads/RPM-GPG-KEY-percona
sudo mv RPM-GPG-KEY-percona /etc/pki/rpm-gpg/
Install Percona via Yum
sudo yum install -y Percona-Server-client-56 Percona-Server-server-56 Percona-Server-devel-56
Start Percona and Setup Root Pass
service mysql start
# then run
/usr/bin/mysql_secure_installation
# setup root password
Install HHVM
# needed to work around libstdc version issue
sudo yum upgrade --setopt=protected_multilib=false --skip-broken
# setup the hop5 repo
cd /etc/yum.repos.d
sudo wget http://www.hop5.in/yum/el6/hop5.repo
# show available versions of hvvm
yum list --showduplicates hhvm
# install latest verison show from list above
yum --nogpgcheck install -y hhvm-3.2.0-1.el6
Install Nginx and PHP-FPM
yum --enablerepo=remi install -y nginx php55w-fpm php55w-common
Configuring Nginx
# rename the default config as its not needed
sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.old
# create a new config
vi /etc/nginx/conf.d/server.conf
server {
server_name mydomainname.com www.mydomainname.com;
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log info;
# 504 is a PHP timeout and must be static
# 502 is momentary during a PHP restart and should be treated like maintenance
# other 50x errors are handled by Magento
error_page 502 504 /var/www/mysite/504.html;
listen 80;
#listen 443 ssl;
# if you are using a load balancer uncomment these lines
# header from the hardware load balancers
#real_ip_header X-Forwarded-For;
# trust this header from anything inside the subnet
#set_real_ip_from X.X.X.1/24;
# the header is a comma-separated list; the left-most IP is the end user
#real_ip_recursive on;
# ensure zero calls are written to disk
client_max_body_size 16m;
client_body_buffer_size 2m;
client_header_buffer_size 16k;
large_client_header_buffers 8 8k;
root /var/www/mysite;
index index.php;
fastcgi_read_timeout 90s;
fastcgi_send_timeout 60s;
# ensure zero calls are written to disk
fastcgi_buffers 512 16k;
fastcgi_buffer_size 512k;
fastcgi_busy_buffers_size 512k;
# remove the cache-busting timestamp
location ~* (.+)\.(\d+)\.(js|css|png|jpg|jpeg|gif)$ {
try_files $uri $1.$3;
access_log off;
log_not_found off;
expires 21d;
add_header Cache-Control "public";
}
# do not log static files; regexp should capture alternate cache-busting timestamps
location ~* \.(jpg|jpeg|gif|css|png|js|ico|txt|swf|xml|svg|svgz|mp4|ogg|ogv)(\?[0-9]+)?$ {
access_log off;
log_not_found off;
expires 21d;
add_header Cache-Control "public";
}
# Server
include main.conf;
include security.conf;
}
Create a home for your website
If you don’t already have a place for your website files to live you will need to create one:
sudo mkdir -p /var/www/mysite/
# while you at it create a nice static error page
echo "error page" >> /var/www/mysite/504.html
Setup Nginx for HHVM and Magento
Nginx needs to be told how to work with PHP traffic and forward it via FastCGI to HHVM. Here is a good configuration. You will notice their is some standard rewrites for Magento assets in place.
vi /etc/nginx/main.conf
rewrite_log on;
location / {
index index.php;
try_files $uri $uri/ @handler;
}
location @handler {
rewrite / /index.php;
}
## force www in the URL
if ($host !~* ^www\.) {
#rewrite / $scheme://www.$host$request_uri permanent;
}
## Forward paths like /js/index.php/x.js to relevant handler
location ~ \.php/ {
rewrite ^(.*\.php)/ $1 last;
}
location /media/catalog/ {
expires 1y;
log_not_found off;
access_log off;
}
location /skin/ {
expires 1y;
}
location /js/ {
access_log off;
}
location ~ \.php$ { ## Execute PHP scripts
if (!-e $request_filename) { rewrite / /index.php last; } ## Catch 404s that try_files miss
expires off; ## Do not cache dynamic content
# for this tutorial we are going to use a unix socket
# but if HHVM was running on another host we could forego unix socket
# in favor of an IP address and port number as follows:
#fastcgi_pass 127.0.0.1:8080;
fastcgi_pass unix:/var/run/hhvm/sock;
fastcgi_index index.php;
#fastcgi_param HTTPS $fastcgi_https;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# if you need to explictly specify a store code for Magento do it here
# this is useful if you are running multiple stores with different hostnames
#fastcgi_param MAGE_RUN_CODE default;
#fastcgi_param MAGE_RUN_TYPE store;
include fastcgi_params; ## See /etc/nginx/fastcgi_params
fastcgi_keep_conn on; #hhvm param
}
Next we need to setup our security configuration:
vi /etc/nginx/security.conf
## General Magento Security
location /app/ { deny all; }
location /includes/ { deny all; }
location /lib/ { deny all; }
location /media/downloadable/ { deny all; }
location /pkginfo/ { deny all; }
location /report/config.xml { deny all; }
location /var/ { deny all; }
## Disable .htaccess and other hidden files
location /\. {
return 404;
}
## Disable all methods besides HEAD, GET and POST.
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
HHVM Configuration
vi /etc/hhvm/server.hdf
PidFile = /var/run/hhvm/pid
Server {
Port = 8080
SourceRoot = /var/www/mysite
DefaultDocument = index.php
}
Log {
Level = Warning
AlwaysLogUnhandledExceptions = true
RuntimeErrorReportingLevel = 8191
UseLogFile = true
UseSyslog = false
File = /var/log/hhvm/error.log
Access {
* {
File = /var/log/hhvm/access.log
Format = %h %l %u % t \"%r\" %>s %b
}
}
}
Repo {
Central {
Path = /var/log/hhvm/.hhvm.hhbc
}
}
#include "/usr/share/hhvm/hdf/static.mime-types.hdf"
StaticFile {
FilesMatch {
* {
pattern = .*\.(dll|exe)
headers {
* = Content-Disposition: attachment
}
}
}
Extensions : StaticMimeTypes
}
MySQL {
TypedResults = false
}
HHVM Fast-CGI support
HHVM will need to start with Fast-CGI support so Nginx can forward PHP request to it. We also need to edit the start up script to make HHVM use a unix socket. To do this edit the following file:
vi /etc/init.d/hhvm
I’ve only made a few changes to the start function start function to enable zend sorting per [Daniel Sloof](https://github.com/danslo) recommendation. I’ve also change the shutdown to kill the proper pid file (/var/run/hhvm/hhvm.pid). Here is the full init file:
#!/bin/bash
#
# /etc/rc.d/init.d/hhvm
#
# Starts the hhvm daemon
#
# chkconfig: 345 26 74
# description: HHVM (aka the HipHop Virtual Machine) is an open-source virtual machine designed for executing programs written in Hack and PHP
# processname: hhvm
### BEGIN INIT INFO
# Provides: hhvm
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop hhvm
# Description: HHVM (aka the HipHop Virtual Machine) is an open-source virtual machine designed for executing programs written in Hack and PHP
### END INIT INFO
# Source function library.
. /etc/init.d/functions
start() {
echo -n "Starting hhvm: "
/usr/bin/hhvm --config /etc/hhvm/server.hdf --user apache --mode daemon -vServer.Type=fastcgi -vServer.FileSocket=/var/run/hhvm/sock -vEval.EnableZendSorting=1
touch /var/lock/subsys/hhvm
}
stop() {
echo -n "Shutting down hhvm: "
killproc -p /var/run/hhvm/pid
rm -f /var/lock/subsys/hhvm
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
if [ ! -f /var/run/hhvm/pid ]; then
echo "hhvm not is running"
else
echo "hhvm is running"
fi
;;
restart)
stop
start
;;
reload|condrestart|probe)
echo "$1 - Not supported."
;;
*)
echo "Usage: hhvm {start|stop|status|reload|restart[|probe]"
exit 1
;;
esac
exit $?
Starting HHVM
As you can see if the init file for HHVM we started it with the user “apache”. So before starting HHVM make sure the directory your files are stored is owned by that group. Yes I know I’m being lazy and probably should create a new user and group running hhvm.
sudo chown apache:apache /var/www -R
We also need to give HHVM the permissions to:
mkdir -p /var/run/hhvm
chown apache:apache /var/run/hhvm
chmod 775 /var/run/hhvm
Finally we can start Nginx PHP-FPM and HHVM.
service nginx start
service php-fpm start
service hhvm start
The famous phpinfo() function will not work on HHVM but there is a very nice HHVM equivalent. Lets download it for fun:
cd /var/www/mysite/
wget https://gist.githubusercontent.com/ck-on/67ca91f0310a695ceb65/raw/hhvminfo.php
HHVM admin
HHVM has an admin tool you can use to get stats – [AdminServer](http://hhvm.com/blog/521/the-adminserver). If you want to see what is available you can create the following file:
vi /etc/nginx/conf.d/admin.conf
server {
# hhvm admin
listen 8889;
location ~ {
fastcgi_pass 127.0.0.1:8888;
include fastcgi_params;
}
}
Then add this block to your hhvm configuration:
vi /etc/hhvm/config.hdf
AdminServer {
Port = 8888
Password = mySecretPassword
}
Some additional tuning
It is also recommended to use “pm = static” mode (instead of “pm = dynamic”) if you decide to
dedicate a server for PHP-FPM exclusively, as there is no need for dynamic allocation of resources to PHP-FPM. The “pm” part of the configuration is more or less the same as if you were to configure Apache.
vi /etc/php-fpm.d/www.conf
# make these changes
pm = static
pm.max_children = 48
pm.start_servers = 8
pm.min_spare_servers = 8
pm.max_spare_servers = 8
pm.max_requests = 40000
request_terminate_timeout = 120
catch_workers_output = yes
security.limit_extensions = .php .html .phtml
vi /etc/php.ini
[PHP]
engine = On
short_open_tag = On
asp_tags = Off
precision = 14
y2k_compliance = On
output_buffering = 4096
zlib.output_compression = Off
implicit_flush = Off
unserialize_callback_func =
serialize_precision = 100
allow_call_time_pass_reference = Off
safe_mode = Off
safe_mode_gid = Off
safe_mode_include_dir =
safe_mode_exec_dir =
safe_mode_allowed_env_vars = PHP_
safe_mode_protected_env_vars = LD_LIBRARY_PATH
disable_functions =
disable_classes =
expose_php = On
max_execution_time = 90
max_input_time = 120
memory_limit = 512M
max_input_vars = 25000
error_reporting = E_ALL & ~E_DEPRECATED
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
html_errors = Off
variables_order = "GPCS"
request_order = "GP"
register_globals = Off
register_long_arrays = Off
register_argc_argv = Off
auto_globals_jit = On
post_max_size = 64M
magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off
auto_prepend_file =
auto_append_file =
default_mimetype = "text/html"
doc_root =
user_dir =
enable_dl = Off
file_uploads = On
upload_max_filesize = 64M
allow_url_fopen = On
allow_url_include = Off
default_socket_timeout = 90
realpath_cache_size = 128k
realpath_cache_ttl = 86400
[Pdo_mysql]
pdo_mysql.cache_size = 2000
[Syslog]
define_syslog_variables = Off
[mail function]
SMTP = localhost
smtp_port = 25
sendmail_path = /usr/sbin/sendmail -t -i
mail.add_x_header = On
[SQL]
sql.safe_mode = Off
[ODBC]
odbc.allow_persistent = On
odbc.check_persistent = On
odbc.max_persistent = -1
odbc.max_links = -1
odbc.defaultlrl = 4096
odbc.defaultbinmode = 1
[MySQL]
mysql.allow_persistent = Off
mysql.max_persistent = -1
mysql.max_links = -1
mysql.default_port =
mysql.default_socket =
mysql.default_host =
mysql.default_user =
mysql.default_password =
mysql.connect_timeout = 60
mysql.trace_mode = Off
[MySQLi]
mysqli.max_links = -1
mysqli.default_port = 3306
mysqli.default_socket =
mysqli.default_host =
mysqli.default_user =
mysqli.default_pw =
mysqli.reconnect = Off
[PostgresSQL]
pgsql.allow_persistent = On
pgsql.auto_reset_persistent = Off
pgsql.max_persistent = -1
pgsql.max_links = -1
pgsql.ignore_notice = 0
pgsql.log_notice = 0
[Sybase-CT]
sybct.allow_persistent = On
sybct.max_persistent = -1
sybct.max_links = -1
sybct.min_server_severity = 10
sybct.min_client_severity = 10
[bcmath]
bcmath.scale = 0
[Session]
session.save_handler = files
session.save_path = "/var/lib/php/session"
session.use_cookies = 1
session.use_only_cookies = 1
session.name = PHPSESSID
session.auto_start = 0
session.cookie_lifetime = 0
session.cookie_path = /
session.cookie_domain =
session.cookie_httponly =
session.serialize_handler = php
session.gc_probability = 1
session.gc_divisor = 1000
session.gc_maxlifetime = 1440
session.bug_compat_42 = Off
session.bug_compat_warn = Off
session.referer_check =
session.entropy_length = 0
session.entropy_file =
session.cache_limiter = nocache
session.cache_expire = 180
session.use_trans_sid = 0
session.hash_function = 0
session.hash_bits_per_character = 5
url_rewriter.tags = "a=href,area=href,frame=src,input=src,form=fakeentry"
[MSSQL]
mssql.allow_persistent = On
mssql.max_persistent = -1
mssql.max_links = -1
mssql.min_error_severity = 10
mssql.min_message_severity = 10
mssql.compatability_mode = Off
mssql.secure_connection = Off
[Tidy]
tidy.clean_output = Off
[soap]
soap.wsdl_cache_enabled=1
soap.wsdl_cache_dir="/tmp"
soap.wsdl_cache_ttl=86400
Installing Redis
Since we are going to be using Redis for our store lets make sure to install it.
yum install -y gcc
wget http://download.redis.io/redis-stable.tar.gz
tar xvzf redis-stable.tar.gz
cd redis-stable
make
make install
# give Redis a home
mkdir -p /var/redis
Redis startup scripts
We are going to be running 3 Redis instances for Magento sessions, cache, and FPC. Each redis session is on a different port. To do this we need startup scripts. Here is my startup scripts. As you can see I’m using unix sockets and allocating 500mb for sessions, 1gb for cache, and 2gb for FPC.
Sessions on port 8302
vi /etc/redis/8302.conf
daemonize yes
pidfile /var/run/redis_8302.pid
port 8302
unixsocket /var/run/redis_8302.sock
unixsocketperm 777
timeout 0
tcp-keepalive 0
loglevel notice
logfile /var/log/redis_8302.log
databases 2
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression no
rdbchecksum yes
dbfilename dump.rdb
dir /var/redis/8302
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
maxmemory-policy volatile-lru
maxmemory 500mb
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
Cache on port 8402
vi /etc/redis/8402.conf
daemonize yes
pidfile /var/run/redis_8402.pid
port 8402
unixsocket /var/run/redis_8402.sock
unixsocketperm 777
timeout 0
tcp-keepalive 0
loglevel notice
logfile /var/log/redis_8402.log
databases 2
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression no
rdbchecksum yes
dbfilename dump.rdb
dir /var/redis/8402
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
maxmemory-policy volatile-lru
maxmemory 1gb
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
FPC Cache on port 8502
vi /etc/redis/8502.conf
daemonize yes
pidfile /var/run/redis_8502.pid
unixsocket /var/run/redis_8502.sock
unixsocketperm 777
port 8502
timeout 0
tcp-keepalive 0
loglevel notice
logfile /var/log/redis_8502.log
databases 2
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression no
rdbchecksum yes
dbfilename dump.rdb
dir /var/redis/8502
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
maxmemory-policy volatile-lru
maxmemory 2gb
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
Redis Startup scripts
We need a way to start our servers. We can do this by creating startup scripts for it. Here are my 3 Redis startup scripts.
vi /etc/init.d/redis_8302
#!/bin/sh
#
# redis Startup script for Redis Server
#
# chkconfig: - 90 10
# description: Redis is an open source, advanced key-value store.
#
# processname: redis-server
REDISPORT=8302
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli
PIDFILE=/var/run/redis_8302.pid
CONF="/etc/redis/8302.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
exit 0
vi /etc/init.d/redis_8402
#!/bin/sh
#
# redis Startup script for Redis Server
#
# chkconfig: - 90 10
# description: Redis is an open source, advanced key-value store.
#
# processname: redis-server
REDISPORT=8402
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli
PIDFILE=/var/run/redis_8402.pid
CONF="/etc/redis/8402.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
exit 0
vi /etc/init.d/redis_8502
#!/bin/sh
#
# redis Startup script for Redis Server
#
# chkconfig: - 90 10
# description: Redis is an open source, advanced key-value store.
#
# processname: redis-server
REDISPORT=8502
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli
PIDFILE=/var/run/redis_8502.pid
CONF="/etc/redis/8502.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
while [ -x /proc/${PID} ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
exit 0
Set the file permissions on the startup scripts:
cd /etc/init.d/
chmod 755 redis_*
mkdir -p /var/redis/8302
mkdir -p /var/redis/8402
mkdir -p /var/redis/8502
chmod 775 /var/redis/8302
chmod 775 /var/redis/8402
chmod 775 /var/redis/8502
Starting Redis servers
sh /etc/init.d/redis_8302 start
sh /etc/init.d/redis_8402 start
sh /etc/init.d/redis_8502 start
You can verify it is running by using the redis-cli tool:
redis-cli -p 8302
redis-cli -p 8402
redis-cli -p 8502
Apache JMeter Benchmarking
Magento has release a beta version of performance testing scripts that are available here. I followed the instructions in the accompanying PDF document, but had some troubles when I was trying to run the JMeter script on my local OSX machine. Magento doesn’t mention it in the documentation but you also need to add the JMeter plugins.
When you are ready to run the benchmark simply issue:
jmeter -n -t benchmark.jmx -Jhost=beepaux03.mmm.com -Jbase_path=/ -Jusers=100 -Jramp_period=300 -Jreport_save_path=./
Or you can use the GUI version of JMeter and get the fancy charts and graphs. You just need to enable the charts and set the parameters. I’m a rookie at JMeter so I’m sure I have lots to learn.
Here are the OSX instructions for those using homebrew:
brew install jmeter
wget http://jmeter-plugins.org/downloads/file/JMeterPlugins-Standard-1.2.0.zip
wget http://jmeter-plugins.org/downloads/file/JMeterPlugins-Extras-1.2.0.zip
unzip JMeterPlugins-Extras-1.2.0
yes | cp -R JMeterPlugins-Extras-1.2.0/lib /usr/local/Cellar/jmeter/2.11/libexec/lib
yes | cp -R JMeterPlugins-Standard-1.2.0/lib /usr/local/Cellar/jmeter/2.11/libexec/lib
By default the distribution is as follows:
- Browsing, adding items to a cart and abandoning the cart: 62%
- Just browsing: 30%
- Browsing, adding items to a cart and checking out as a guest: 4%
- Browsing, adding items to a cart and checking out as a registered customer: 4%.
If your interested the inter-workings on the Magento JMeter script there is a detailed break down here.
Also to note:
The JMeter java configuration comes with 512 Mo and very little GC tuning. First ensure you set -Xmx option value to a reasonable value regarding your test requirements. Then change MaxNewSize option in jmeter file to respect the original ratio between MaxNewSize and -Xmx.
vi /usr/local/Cellar/jmeter/2.11/libexec/bin/jmeter
# change head param to increase memory
HEAP="-Xms1G -Xmx3G"
And now for the results you have been waiting for:
HHVM / Nginx / Redis / Percona
PHP 5.5.18 / Nginx / Redis / Percona (not using HHVM)
Charted together
In conclusion
I’m still testing HHVM before putting it in production. If you are using HHVM with Magento in production I would love to hear from you. Hit me up on twitter @tegansnyder.