The moment a PHP application grows to run on more servers, normally people will see problems caused by PHP sessions. If the application is not persistent you are lucky and don’t care about this, but if not you will quickly see this regardless of how good the load balancer you use is handling stickiness (sending the users to the same real server), this will slowly become a major issue. There are various solutions that can be used to store PHP sessions in a shared location, but I want to present today one solution that is very simple to implement, yet very efficient and on the long term better suited than using a database backend for this: using memcache to store the sessions.
The pecl memcache php extension has supported for a long time the memcache session.save_handler, but with the release 3.0.x (still in beta at this time) this brings in a set of interesting features for us:
- UDP support
- Binary protocol support
- Non-blocking IO using select()
- Key and session redundancy (values are written to N mirrors)
- Improved error reporting and failover handling
Installing the php memcache module is very simple and can be done either by using distribution repositories (the version we want to use 3.0.x will probably not be available) or by using pecl or manual compilation:
pecl install memcache-3.0.4
wget http://pecl.php.net/get/memcache-3.0.4.tgz tar xvfz memcache-3.0.4.tgz cd memcache-3.0.4 phpize ./configure make make install
Finally, we need to activate the module in php.ini. I normally prefer to create a new file for this memcache.ini inside the include directory of the php build (for ex. in debian this is under /etc/php5/conf.d/memcache.ini) like this:
extension=memcache.so memcache.allow_failover = 1 memcache.redundancy = 1 memcache.session_redundancy = 2
To use memcached to store the php sessions we will have to edit php.ini and replace the default file handler settings with something like:
; Use memcache as a session handler session.save_handler = memcache ; Use a comma separated list of server urls to use for storage: session.save_path="udp://<memcache_server>:11211?persistent=1&weight=1&timeout=1&retry_interval=15"
or if we don’t want to use this serverwide, we can just define it inside a php block like this:
$session_save_path = "tcp://<memcache_server1>:11211?persistent=1&weight=1&timeout=1&retry_interval=15, tcp://<memcache_server2>:11211"; ini_set('session.save_handler', 'memcache'); ini_set('session.save_path', $session_save_path);
Note: as you can see I used in the above example tcp and udp also. Please be sure that your memcached server has udp support enabled if you want to use that. Also ensure that the web server can connect to the memcache server/port (use proper firewall rules to allow this) in order for this to work.
After restarting apache, it will start using memcache to store the php sessions. If redundancy is needed (why not?) we will probably want to use the internal php memcache support to save the sessions to more servers, or if you prefer you can use an external solution to replicate the memcached server data - repcached.