Redis is used to realize Session function

  • 2020-05-15 02:31:03
  • OfStack

0. What is Redis

Redis is an open source ANSI C language, web support, memory-based and persistent logging, Key-Value database, and API in multiple languages

1. Comparison with other user state saving schemes

1 generally, session or cookie are used for user state in development, both of which have various advantages and disadvantages.

Session: easy to lose in InProc mode and cause concurrency problems. Using SQLServer or SQLServer mode consumes performance

Cookie can easily expose some user information, and encryption and decryption also consume performance.

Redis solves several problems with this solution,

The access speed of Redis is fast.

. User data is not easy to lose.

It is easy to support the cluster when there are many users.

. Can view online users.

. Can achieve user 1 place login. (implemented by code, more on that later)

6. Support persistence. (probably not)

2. Implementation ideas

1. We know that session actually saves an sessionid in cookie, and sessionid is sent to the server every time the user visits, and the server searches the state data corresponding to the user through ID.

Here, I also define an sessionid in cookie. When the program needs to obtain user status, it will use sessionid as key and look it up in Redis.

2. At the same time, session supports users to recycle session without access at a certain time.

This is supported by Keys's support for expiration time feature, but in the case of renewals, it is up to the program itself to intercept requests and call this method (demo has an example)

The code description begins below

3.Redis call interface

First reference ServiceStack with DLL.

Add the configuration in web.config, which sets the Redis call address separated by [,] for each service. The host is written in bit 1


 <appSettings> 
  <!-- each Redis In between , segmentation . The first 1 Each must be the host -->
  <add key="SessionRedis" value="127.0.0.1:6384,127.0.0.1:6384"/> 
 </appSettings>

Initialization configuration


static Managers()
    {
      string sessionRedis= ConfigurationManager.AppSettings["SessionRedis"];
      string timeOut = ConfigurationManager.AppSettings["SessionRedisTimeOut"];

      if (string.IsNullOrEmpty(sessionRedis))
      {
        throw new Exception("web.config  The lack of configuration SessionRedis, each Redis In between , segmentation . The first 1 Each must be the host ");
      }

      if (string.IsNullOrEmpty(timeOut)==false)
      {
        TimeOut = Convert.ToInt32(timeOut);
      }

      var host = sessionRedis.Split(char.Parse(","));
      var writeHost = new string[] { host[0] };
      var readHosts = host.Skip(1).ToArray();

      ClientManagers = new PooledRedisClientManager(writeHost, readHosts, new RedisClientManagerConfig
      {
        MaxWritePoolSize = writeReadCount,// "Write" link pool number of links 
        MaxReadPoolSize = writeReadCount,// The number of "read" links in the link pool 
        AutoStart = true
      });
    }

I wrote a delegate for control


 /// <summary>
    ///  write 
    /// </summary>
    /// <typeparam name="F"></typeparam>
    /// <param name="doWrite"></param>
    /// <returns></returns>
    public F TryRedisWrite<F>(Func<IRedisClient, F> doWrite)
    {
      PooledRedisClientManager prcm = new Managers().GetClientManagers();
      IRedisClient client = null;
      try
      {
        using (client = prcm.GetClient())
        {
          return doWrite(client);
        }
      }
      catch (RedisException)
      {
        throw new Exception("Redis Write to abnormal .Host:" + client.Host + ",Port:" + client.Port);
      }
      finally
      {
        if (client != null)
        {
          client.Dispose();
        }
      }
    }

An example of a call to the other specific source code


   /// <summary>
    ///  In order to Key/Value The form stores objects into the cache 
    /// </summary>
    /// <typeparam name="T"> Object classes </typeparam>
    /// <param name="value"> The collection to write to </param>
    public void KSet(Dictionary<string, T> value)
    {
      Func<IRedisClient, bool> fun = (IRedisClient client) =>
      {
        client.SetAll<T>(value);
        return true;
      };

      TryRedisWrite(fun);
    }

4. Implement Session

Write an sessionid for cookie as described above


  /// <summary>
  ///  User state management 
  /// </summary>
  public class Session
  {
    /// <summary>
    ///  Initialize the 
    /// </summary>
    /// <param name="_context"></param>
    public Session(HttpContextBase _context)
    {
      var context = _context;
      var cookie = context.Request.Cookies.Get(SessionName);
      if (cookie == null || string.IsNullOrEmpty(cookie.Value))
      {
        SessionId = NewGuid();
        context.Response.Cookies.Add(new HttpCookie(SessionName, SessionId));
        context.Request.Cookies.Add(new HttpCookie(SessionName, SessionId));
      }
      else
      {
        SessionId = cookie.Value;
      }
    }

  }

To access the user's methods


    /// <summary>
    ///  Get the current user information 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public object Get<T>() where T:class,new()
    {
      return new RedisClient<T>().KGet(SessionId);
    }

    /// <summary>
    ///  Is the user online 
    /// </summary>
    /// <returns></returns>
    public bool IsLogin()
    {
      return new RedisClient<object>().KIsExist(SessionId);
    }

    /// <summary>
    ///  The login 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="obj"></param>
    public void Login<T>(T obj) where T : class,new()
    {
      new RedisClient<T>().KSet(SessionId, obj, new TimeSpan(0, Managers.TimeOut, 0));
    }
 

Renewal of 6.

By default, the user does not access the logout status of the user for more than 30 minutes, so the logout time of the user is delayed by 30 minutes for each visit

This requires a call to the renewal method of Redis


    /// <summary>
    ///  delay 
    /// </summary>
    /// <param name="key"></param>
    /// <param name="expiresTime"></param>
    public void KSetEntryIn(string key, TimeSpan expiresTime)
    {
      Func<IRedisClient, bool> fun = (IRedisClient client) =>
      {
        client.ExpireEntryIn(key, expiresTime);
        return false;
      };

      TryRedisWrite(fun);
    }


  After encapsulation 
/// <summary>
///  renewal 
/// </summary>
public void Postpone()
{
new RedisClient<object>().KSetEntryIn(SessionId, new TimeSpan(0, Managers.TimeOut, 0));
}

Here I take advantage of ActionFilter in MVC3 to intercept all user requests


namespace Test
{
  public class SessionFilterAttribute : ActionFilterAttribute
  {
    /// <summary>
    ///  Each request is renewed 
    /// </summary>
    /// <param name="filterContext"></param>
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
      new Session(filterContext.HttpContext).Postpone();
    }
  }
}

Register 1 in Global.asax


public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
      filters.Add(new SessionFilterAttribute());
    }

    protected void Application_Start()
    {
      RegisterGlobalFilters(GlobalFilters.Filters);
    } 

5. Method of invocation

To facilitate the call to borrow the new feature in 4.0, add an extended property to Controller


static Managers()
    {
      string sessionRedis= ConfigurationManager.AppSettings["SessionRedis"];
      string timeOut = ConfigurationManager.AppSettings["SessionRedisTimeOut"];

      if (string.IsNullOrEmpty(sessionRedis))
      {
        throw new Exception("web.config  The lack of configuration SessionRedis, each Redis In between , segmentation . The first 1 Each must be the host ");
      }

      if (string.IsNullOrEmpty(timeOut)==false)
      {
        TimeOut = Convert.ToInt32(timeOut);
      }

      var host = sessionRedis.Split(char.Parse(","));
      var writeHost = new string[] { host[0] };
      var readHosts = host.Skip(1).ToArray();

      ClientManagers = new PooledRedisClientManager(writeHost, readHosts, new RedisClientManagerConfig
      {
        MaxWritePoolSize = writeReadCount,// "Write" link pool number of links 
        MaxReadPoolSize = writeReadCount,// The number of "read" links in the link pool 
        AutoStart = true
      });
    }

0

A method is called


static Managers()
    {
      string sessionRedis= ConfigurationManager.AppSettings["SessionRedis"];
      string timeOut = ConfigurationManager.AppSettings["SessionRedisTimeOut"];

      if (string.IsNullOrEmpty(sessionRedis))
      {
        throw new Exception("web.config  The lack of configuration SessionRedis, each Redis In between , segmentation . The first 1 Each must be the host ");
      }

      if (string.IsNullOrEmpty(timeOut)==false)
      {
        TimeOut = Convert.ToInt32(timeOut);
      }

      var host = sessionRedis.Split(char.Parse(","));
      var writeHost = new string[] { host[0] };
      var readHosts = host.Skip(1).ToArray();

      ClientManagers = new PooledRedisClientManager(writeHost, readHosts, new RedisClientManagerConfig
      {
        MaxWritePoolSize = writeReadCount,// "Write" link pool number of links 
        MaxReadPoolSize = writeReadCount,// The number of "read" links in the link pool 
        AutoStart = true
      });
    }

1

6. Code download

Click on the download

7. Subsequent

SessionManager includes methods to get the number of user lists, log out a user, get user information according to user ID, list of online user objects, list of online user SessionId, etc

The login function at user 1 will be implemented later


Related articles: