HTTP Sessions using Redis in Yii

This post serves two main goals:

  1. Explain how to use Redis as a cache and http session storage in a Yii project
  2. Explain why it’s possible to do it only with configuration due to Object Orientation and Design Patterns

What is Redis

Redis is an open source key-value store featuring some interesting data structures, such as hashes, lists, sets, etc.. In its simplest deployment it can be used just like a memcached.

Although there’s been quite some debate in the Internet supporting this or that key-value store, this post assumes you’ve chosen redis for your own reasons and you want to integrate it in your Yii application, without suggesting or explaining why you should do it.

Redis and PHP

First thing you need to do is get an extension/library to “bind” Redis with PHP. The suggested solution here is PRedis. Predis is a library written in the PHP language. It allows you to optionally utilize the phpiredis extension (written in C) to gain some performance at the protocol implementation level, but it generally works out-of-the-box on any platform that can support PHP 5.3 (including windows, etc.)

Predis installation: Make sure you have PHP 5.3 or later, download it the package and extract it into your Yii project, let’s say into /protected/vendors/predis-0-8-3

Note: Mind the dashes between the version numbers. You may not use dots in folder names since Yii will interpret them as path seperators.

Note: The reason for not choosing some of the existing native extensions written in C (eg. phpredis or phpiredis mentioned above) is simple: We need to run everything in an OS-independent fashion and native extensions, although featuring some performance boost, usually come with the undesired cost of having to compile them yourself on your “non-standard” platform (not linux). Again, this is not a reason or beginning for a debate among PHP redis extensions and libraries. It’s just the demonstration of our choice.

CRedisCache: A PRedis-based Yii Cache component

Having brought redis into our PHP world using Predis, we now want to integrate Predis with Yii. Gustavo Salom has written a simple, yet effective, Yii extension which extends Yii’s CCache utilizing predis.

First you download the extension and extract it into your Yii project, e.g. /protected/extensions/CRedisCache-2012-05-27

You then define one or more predis cache components in your Yii configuration file (i.e. /protected/config/main.php or so).

You can always replace your default “cache” component to use redis, or you may wish to keep your existing cache (might be for example a file cache) as is and add redis cache as an extra “redisCache” component. This is how the “multi-cache” configuration would look:

	'cache' => array(
		'class' => 'system.caching.CFileCache',
	),
	'redisCache'=>array(
		'class'=>'ext.CRedisCache-2012-05-27.CRedisCache',
		'predisPath'=>'application.vendors.predis-0-8-3.lib.Predis',

		//if you dont set up the servers options it will use the default one
		//"host=>'127.0.0.1',port=>6379"
		'servers'=>array(
				array(
					'database'=>0,
					'host'=>'redis.host.goes.here',
					'port'=>6379,
				),
		),
	),

CRedisCache and HTTP Sessions

Having defined our redisCache component which uses redis for caching, we can now combine it with Yii’s CCacheHttpSession to make Yii store sessions in redis. We only need to add the following configuration which overrides default Yii Session Management (i.e. the default ‘session’ component):

	'session'=>array(
		'class' => 'CCacheHttpSession',
		'cacheID' => 'redisCache',
	),

Explaining the magic

I’m not sure how many of you realized what happened: We just made our Yii application store its sessions in predis without writing/patching/customizing a single line of code.

The short explanation/reason is simple: A framework utilizing Object Orientated Programming and Design Patterns.

You see, the trick or value behind frameworks (Yii in our case) is not that they just offer you a set of ready-to-use functions and methods. This is what a simple library can do. Frameworks should offer extensibility. This is a key to getting the most out of your framework. Unfortunately programmers and engineers tend to ignore or underestimate this key concept. Let’s see where all this OOP and DP is hidden in our case.

The CCache Interface

Yii provides a caching infrastructure which is based on three OO elements:

  1. The ICache interface: Provides an interface which other components will rely on if the want to cache stuff. It makes sure your cache component (implementing the ICache interface) provides (caching) methods such as get(), set(), add(), delete(), etc..
  2. The IApplicationComponent interface: Provides an interface for defining and initializing a component by declaring its name, class and configuration in the /protected/config/main.php file.
  3. The CCache base class: Provides a base implementation of both above interfaces.

How are all these useful for CRedisCache:

  1. CRedisCache extends CCache: This lets CRedisCache inherit all base functionality and implicitly conform to the ICache and IApplicationComponent.
  2. Overriding the ICache methods allows the implementation of the redis caching.
  3. The IApplicationComponent part allows us to define the ‘redisCache’ configuration item and use it in our code by calling Yii::app()->redisCache, i.e. through the Yii::app() singleton.

The CHttpSession Base Class

A common use of key-value stores and caches is HTTP session storage. PHP itself provides a flexible session handler interface for implementing your own storage functionality for HTTP sessions. Yii developers, on their side, provide the CHttpSession, the base Yii implementation of PHP’s session handler interface.

But it’s not just that. The Yii framework takes it a step further and gives you the CCacheHttpSession which is practically an object adapter between the CHttpSession and ICache:

  1. Being a subclass of CHttpSession, CCacheHttpSession it itself a valid implementation of PHP’s session handler interface.
  2. Also, it can just substitute the default ‘session’ application component and be defined in the config.php with its own mandatory set of parameters.
  3. Its major parameter is the ‘cacheID’, which is any component implementing the ICache interface, defined anywhere in the configuration.
  4. Acting as an adapter, the CCacheHttpSession adapts ICache methods to serve the purposes of the PHP Session Handler Interface, allowing virtually any implementation of a cache to be also used as an HTTP Session Handler!

Conclusions

Not much for the epilog. Just compare how much money you’ll save by writing and patching your procedural “super-effective ultra-fast” code compared to the money you’re earning by having your programmers work on a component for so little time.

This is why programming skills (just “writing code in X language) are necessary but not sufficient for good, fast, secure, extensible, readable and ultimately profitable software: You need to get to the upper layer, you need OO and Design Patterns 😉

Tagged with: , , , , , , ,
Posted in PHP, Yii
4 comments on “HTTP Sessions using Redis in Yii
  1. Tom Fotherby says:

    Since Yii 1.1.14 (11/Aug/13) things are even easier because CRedisCache is part of the Yii core – i.e. you no longer need to install the “rediscache” Yii extension. For more details, see http://www.yiiframework.com/news/75/yii-1-1-14-is-released/

    Thanks for this blog post.

    • cottonaf says:

      @Tom, you are correct that yii does include the CRedisCache built in, however it is not based on PRedis, which may or may not matter.

      If one is planning on a single caching server it could not be easier and is basically done already. If, however, one needs the ability to add multiple caching servers it does not seem to be possible without extending the yii class.

      I was really searching for something like Redis Cluster but am happy enough with the PRedis implementation since it approximates memcached, allowing some cache misses in the event of a failure but not a complete site failure. In my specific case, it would result in a user being logged out, but for our application it is not catastrophic.

      Thanks for sharing the yii info.

    • cottonaf says:

      One last thing–after actually implementing the solution I had suggested I had to significantly rework the yii extension in a couple of ways:

      1) I had to point the component predisPath to the autoloader file.
      2) if I pass in multiple servers, or nodes, a failed node results in an exception which I did not expect. I had to get the PredisCluster instance and ping all nodes to determine their status and catch Predis\{?}\CommunicationException, the question mark is what I can’t remember at the moment. If I encounter an exception I extract the connection from the error and remove that from the pool.

      In short: remove failed servers from pool by pinging before using each time. In retrospect I would find another solution. I chose Predis because it does not use an LRU purging technique as memcached does and that made me feel better. In practice, I wanted it to behave exactly as memcached.

      I hope this helps someone.

Leave a comment

In Archive