27/05/2017 - SYMFONY, REDIS
In this example we are going to see how redis can be used in symfony applications. I'll implement some basic features but you can extend it as you wish. You can see all commands here and documentation here.
Redis is an in-memory key-value cache and data store (also referred as NoSQL database). It can persist it's state to disk which helps recover data after restart unlike memcached. Since it stores data in memory, size of data cannot exceed the total memory space on the system. Nowadays, many prefer redis over memcached and MongoDB especially for performance reasons. It can store strings, hashes, lists, sets, sorted sets and more whereas memcached only supports strings.
list
but it has no order and each element appears only once.set
but values stored in set
and ordered/ranked. Each member is associated with score and used to order the set
from the smallest score to the largest.http://192.168.50.10:8081/app_dev.php/redis/set?key=a&value=1
http://192.168.50.10:8081/app_dev.php/redis/set?key=a&value=1&ttl=10
http://192.168.50.10:8081/app_dev.php/redis/get?key=a
http://192.168.50.10:8081/app_dev.php/redis/ttl?key=a
http://192.168.50.10:8081/app_dev.php/redis/persist?key=a
http://192.168.50.10:8081/app_dev.php/redis/delete?key=a
http://192.168.50.10:8081/app_dev.php/redis/expire?key=a&ttl=10
$ sudo apt-get update
$ sudo apt-get install redis-server
$ sudo service redis-server status
Configuration file can be found in /etc/redis/redis.conf
.
$ ps aux | grep redis
redis 31748 0.1 0.3 38576 7248 ? Ssl 13:42 0:00 /usr/bin/redis-server 127.0.0.1:6379
vagrant 31917 0.0 0.0 10472 904 pts/0 R+ 13:47 0:00 grep --color=auto redis
As you can see above the host is 127.0.0.1
and the port is 6379
. Use command below to access redis command line tool. Type info
to get information about the redis server and status.
$ redis-cli
127.0.0.1:6379>
127.0.0.1:6379> monitor
1495812054.054517 [0 127.0.0.1:59164] "SET" "a" "1" # Persistent
1495812054.054517 [0 127.0.0.1:59164] "SETEX" "b" "10" "2" # Expire in 10 seconds
As you can above, I've already set two key-value pairs, a
and b
. The monitor
command shows live logs of commands run against redis server.
127.0.0.1:6379> scan 0
1) "0"
2) 1) "a"
3) 2) "b"
127.0.0.1:6379> GET a
"1"
127.0.0.1:6379> TTL a
(integer) -1 # Persistent
127.0.0.1:6379> GET b
"2"
127.0.0.1:6379> TTL b # Run this command 3 seconds later
(integer) 7 # 7 seconds to expire
127.0.0.1:6379> TTL b # Run this command 20 seconds later
(integer) -2 # Expired
If you don't specify TTL while setting key-value pair, the key will never expire as TTL is set to -1
by default so technically this is a persistent operation.
$ sudo apt-get install php5-redis
$ sudo service apache2 restart
Add "ext-redis": "*"
to composer.json and run composer update ext-redis
command.
parameters:
redis.host: 127.0.0.1
redis.port: 6379
services:
app.controller.redis:
class: AppBundle\Controller\RedisController
arguments:
- "@app.util.redis_helper"
services:
app.util.redis_helper:
class: AppBundle\Util\RedisHelper
arguments:
- '%redis.host%'
- '%redis.port%'
namespace AppBundle\Controller;
use AppBundle\Util\RedisHelper;
use RedisException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* @Route("/redis", service="app.controller.redis")
*/
class RedisController
{
private $redisHelper;
public function __construct(RedisHelper $redisHelper)
{
$this->redisHelper = $redisHelper;
}
/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/set")
*
* @return Response
*/
public function setAction(Request $request)
{
$key = $request->query->get('key');
$value = $request->query->get('value');
$ttl = $request->query->get('ttl');
$result = null;
try {
if ($key && $value) {
$this->redisHelper->set($key, $value, $ttl);
$result = ['key' => $key, 'value' => $value, 'ttl' => $ttl];
}
} catch (RedisException $e) {
$result = $e->getMessage();
}
return new Response(json_encode($result));
}
/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/get")
*
* @return Response
*/
public function getAction(Request $request)
{
$key = $request->query->get('key');
$result = null;
try {
if ($key) {
$result = ['key' => $key, 'value' => $this->redisHelper->get($key)];
}
} catch (RedisException $e) {
$result = $e->getMessage();
}
return new Response(json_encode($result));
}
/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/ttl")
*
* @return Response
*/
public function ttlAction(Request $request)
{
$key = $request->query->get('key');
$result = null;
try {
if ($key) {
$result = ['key' => $key, 'ttl' => $this->redisHelper->getTtl($key)];
}
} catch (RedisException $e) {
$result = $e->getMessage();
}
return new Response(json_encode($result));
}
/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/persist")
*
* @return Response
*/
public function persistAction(Request $request)
{
$key = $request->query->get('key');
$result = null;
try {
if ($key) {
$result = ['key' => $key, 'persist' => $this->redisHelper->persist($key)];
}
} catch (RedisException $e) {
$result = $e->getMessage();
}
return new Response(json_encode($result));
}
/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/expire")
*
* @return Response
*/
public function expireAction(Request $request)
{
$key = $request->query->get('key');
$ttl = $request->query->get('ttl');
$result = null;
try {
if ($key) {
$result = ['key' => $key, 'expire' => $this->redisHelper->expire($key, $ttl)];
}
} catch (RedisException $e) {
$result = $e->getMessage();
}
return new Response(json_encode($result));
}
/**
* @param Request $request
*
* @Method({"GET"})
* @Route("/delete")
*
* @return Response
*/
public function deleteAction(Request $request)
{
$key = $request->query->get('key');
$result = null;
try {
if ($key) {
$result = ['key' => $key, 'expire' => $this->redisHelper->delete($key)];
}
} catch (RedisException $e) {
$result = $e->getMessage();
}
return new Response(json_encode($result));
}
}
Read comments in annotations!
namespace AppBundle\Util;
use Redis;
class RedisHelper
{
const MIN_TTL = 1;
const MAX_TTL = 3600;
/** @var Redis $redis */
private $redis;
private $host;
private $port;
public function __construct($host, $port)
{
$this->host = $host;
$this->port = $port;
}
/**
* Get the value related to the specified key.
*/
public function get($key)
{
$this->connect();
return $this->redis->get($key);
}
/**
* set(): Set persistent key-value pair.
* setex(): Set non-persistent key-value pair.
*/
public function set($key, $value, $ttl = null)
{
$this->connect();
if (is_null($ttl)) {
$this->redis->set($key, $value);
} else {
$this->redis->setex($key, $this->normaliseTtl($ttl), $value);
}
}
/**
* Returns 1 if the timeout was set.
* Returns 0 if key does not exist or the timeout could not be set.
*/
public function expire($key, $ttl = self::MIN_TTL)
{
$this->connect();
return $this->redis->expire($key, $this->normaliseTtl($ttl));
}
/**
* Removes the specified keys. A key is ignored if it does not exist.
* Returns the number of keys that were removed.
*/
public function delete($key)
{
$this->connect();
return $this->redis->del($key);
}
/**
* Returns -2 if the key does not exist.
* Returns -1 if the key exists but has no associated expire. Persistent.
*/
public function getTtl($key)
{
$this->connect();
return $this->redis->ttl($key);
}
/**
* Returns 1 if the timeout was removed.
* Returns 0 if key does not exist or does not have an associated timeout.
*/
public function persist($key)
{
$this->connect();
return $this->redis->persist($key);
}
/**
* The ttl is normalised to be 1 second to 1 hour.
*/
private function normaliseTtl($ttl)
{
$ttl = ceil(abs($ttl));
return ($ttl >= self::MIN_TTL && $ttl <= self::MAX_TTL) ? $ttl : self::MAX_TTL;
}
/**
* Connect only if not connected.
*/
private function connect()
{
if (!$this->redis || $this->redis->ping() != '+PONG') {
$this->redis = new Redis();
$this->redis->connect($this->host, $this->port);
}
}
}