• Blog
  • 3 MINUTES READ

Using Redis to Share Real Time Data among Servers

  • POSTED ON
  • June 22, 2017
  • POSTED BY
  • Muhammad Ahmad
  • POSTED ON June 22, 2017
  • POSTED BY Muhammad Ahmad

vteam #678 has been working on a telephony web application. To increase the capacity (concurrent users) and reliability of the application, the client required load balancing so that the agents (users) can login from any of the three servers. Additionally, the application’s admin panel was required to display and control the status of all the active agents on any of the three....

vteam #678 has been working on a telephony web application. To increase the capacity (concurrent users) and reliability of the application, the client required load balancing so that the agents (users) can login from any of the three servers. Additionally, the application’s admin panel was required to display and control the status of all the active agents on any of the three servers.

As the status of agents was changing continuously, some real time features were required. In addition to it, the real time data (like 100 changes per second) must be stored somewhere so that it could be accessed and manipulated later quickly. The following could be the possible solutions to store real time data but it is recommended not to:

  • Make dozens of database queries per second
  • Share the sessions of the three servers
  • Share the files of the three servers
    .

Solution

After doing some R&D, it was proposed and later decided to use Redis because it supports many data structures including:

  • Strings
  • Sets
  • Lists
  • Hashes, etc.
    .

vteams engineer stored the real time data of the client’s application in Redis Hashes. To hold the database, the application already had a dedicated server. The same server was used for Redis. It was then configured to accept external connections.

To implement the above mentioned solution, setup redis as per the instructions given in the laravel docs. This setup included the “predis/predispackage for Laravel. Since the application had two user guards i.e. “user” and “admin”, it was decided to define a trait which would be responsible for all the communication with Redis.

<?php
namespace App\Traits;

/**
* Class HasStatusInCache
* @package App\Traits
*/
trait HasStatusInCache
{
   /**
    * @var string
    * Default key used to identify user records
    */
   public $cacheKey = 'id';

   /**
    * @return \Illuminate\Redis\Database
    * In case we decide to replace Redis with some other cache driver
    */
   protected function cacheClient()
   {
       return app('redis');
   }

   /**
    * @return string
    * Used to group objects in the Cache
    * e.g. You can set cacheTag for Users as "users" and for Admins as "admins"
    * Helpful when if need all the users or admins at once:
    * $keys = app("redis")->keys("agents*");
    * foreach ( $keys as $key ) {
    *      $users[] = app("redis")->get($key);
    */
   protected function cacheTag()
   {
       return get_called_class();
   }

   /**
    * @return string
    * Actual cache key stored, as a combination of cacheKey and cacheTag
    */
   protected function cacheField()
   {
       $key = $this->{$this->cacheKey};
       return "{$this->cacheTag()}:{$key}";
   }

   /**
    * @return mixed
    * Get all the key value pairs of the hashed data
    */
   public function getCachedStatus()
   {
       return $this->cacheClient()->hgetall($this->cacheField());
   }

   /**
    * @param $data
    * @return mixed
    * Sets the key value pairs provided in $data
    * By default Redis hashes work in a way that $data is set in such a way that only provided keys are over-written.
    * So you only need to provide the fields that you need to set / update
    */
   public function setCachedStatus($data)
   {
       return $this->cacheClient()->hmset($this->cacheField(), $data);
   }

   /**
    * @param $key
    * @return mixed
    * Get the value for a specific key
    */
   public function getCachedStatusValue($key)
   {
       return $this->cacheClient()->hget($this->cacheField(), $key);
   }

   /**
    * @param $key
    * @param $value
    * @return mixed
    * Set the value for a specific key
    */
   public function setCachedStatusValue($key, $value)
   {
       return $this->cacheClient()->hset($this->cacheField(), $key, $value);
   }

}

Now the class (User / Admin) that has status in cache:

class User extends Authenticatable
{
   use HasStatusInCache;

   public function cacheTag()
   {
       return "agents";
   }
Start Using it:
example: when logging in
private function logIn($user)
{
    Auth::login($user);
    …
    // set status to cache
    $user->setCachedStatus($user->toArray());
    …
}

example: when something changes
$user->setCachedStatusValue('station', $station->title);

or
// save the current status to cache
$agentStatusData = [
    'auxCode' => $status->name,
    'auxCodeId' => $status->id
];
$agent->setCachedStatus($agentStatusData);

Conclusion

As a result, the three servers were then able to share their data in an extremely fast data store.

ABOUT THE AUTHOR

Muhammad Ahmad

0 Comments

Leave a Reply

More Related Article
We provide tips and advice on delivering excellent customer service, engaging your customers, and building a customer-centric business.