Session sharing using Redis in distributed

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.SessionState;
using ServiceStack.Redis;
using Com.Redis;

namespace ResidSessionDemo.RedisDemo
{
    public class RedisSession
    {
        private HttpContext context;

        public RedisSession(HttpContext context, bool IsReadOnly, int Timeout)
        {
            this.context = context;
            this.IsReadOnly = IsReadOnly;
            this.Timeout = Timeout;
            //Update cache expiration time
            RedisBase.Hash_SetExpire(SessionID, DateTime.Now.AddMinutes(Timeout));
        }

        /// <summary>
        /// SessionId identifier
        /// </summary>
        public static string SessionName = "Redis_SessionId";

        //
        // Summary:
        // Get the number of items in the session state collection.
        //
        // return result:
        // The number of items in the collection.
        public int Count
        {
            get
            {
                return RedisBase.Hash_GetCount(SessionID);
            }
        }

        //
        // Summary:
        // Get a value indicating whether the session is read-only.
        //
        // return result:
        // true if the session is read-only; otherwise, false.
        public bool IsReadOnly { get; set; }

        //
        // Summary:
        // Get the session's unique identifier.
        //
        // return result:
        // Unique session identifier.
        public string SessionID
        {
            get
            {
                return GetSessionID();
            }
        }

        //
        // Summary:
        // Gets and sets the time (in minutes) allowed between requests before the session state provider terminates the session.
        //
        // return result:
        // Timeout period in minutes.
        public int Timeout { get; set; }

        /// <summary>
        /// Get SessionID
        /// </summary>
        /// <param name="key">SessionId identifier</param>
        /// <returns>HttpCookie值</returns>
        private string GetSessionID()
        {
            HttpCookie cookie = context.Request.Cookies.Get(SessionName);
            if (cookie == null || string.IsNullOrEmpty(cookie.Value))
            {
                string newSessionID = Guid.NewGuid().ToString();
                HttpCookie newCookie = new HttpCookie(SessionName, newSessionID);
                newCookie.HttpOnly = IsReadOnly;
                newCookie.Expires = DateTime.Now.AddMinutes(Timeout);
                context.Response.Cookies.Add(newCookie);
                return "Session_"+newSessionID;
            }
            else
            {
                return "Session_"+cookie.Value;
            }
        }

        //
        // Summary:
        // Get or set the session value by name.
        //
        // Parameters:
        //   name:
        // The key name of the session value.
        //
        // return result:
        // The session state value with the specified name; or null if the item does not exist.
        public object this[string name]
        {
            get
            {
                return RedisBase.Hash_Get<object>(SessionID, name);
            }
            set
            {
                RedisBase.Hash_Set<object>(SessionID, name, value);
            }
        }

        // Summary:
        // Determine if the specified key exists in the session
        //
        // Parameters:
        //   name:
        // key value
        //
        public bool IsExistKey(string name)
        {
            return RedisBase.Hash_Exist<object>(SessionID, name);
        }

        //
        // Summary:
        // Add a new item to the session state collection.
        //
        // Parameters:
        //   name:
        // The name of the item to add to the session state collection.
        //
        //   value:
        // The value of the item to add to the session state collection.
        public void Add(string name, object value)
        {
            RedisBase.Hash_Set<object>(SessionID, name, value);
        }
        //
        // Summary:
        // Remove all keys and values ​​from the session state collection.
        public void Clear()
        {
            RedisBase.Hash_Remove(SessionID);
        }

        //
        // Summary:
        // Remove the item from the session state collection.
        //
        // Parameters:
        //   name:
        // The name of the item to remove from the session state collection.
        public void Remove(string name)
        {
            RedisBase.Hash_Remove(SessionID,name);
        }
        //
        // Summary:
        // Remove all keys and values ​​from the session state collection.
        public void RemoveAll()
        {
            Clear();
        }
    }
}

 The following is a way to achieve a similar way of using Session["UserId"] directly in the cs file. My MyPage class inherits Page and implements its own logic. It mainly does two things: 1: Initialize RedisSession 2: Implement unified login authentication, OnPreInit method It judges whether the user is logged in, if not, it jumps to the login interface

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;

namespace ResidSessionDemo.RedisDemo
{
    /// <summary>
    /// Customize Page to achieve the following functions
    /// 1. Initialize RedisSession
    /// 2. Implement page login verification, if you inherit this class, you can implement login verification for all pages
    /// </summary>
    public class MyPage:Page
    {
        private RedisSession redisSession;

        /// <summary>
        /// RedisSession
        /// </summary>
        public RedisSession RedisSession
        {
            get
            {
                if (redisSession == null)
                {
                    redisSession = new RedisSession(Context, true, 20);
                }
                return redisSession;
            }
        }

        protected override void OnPreInit(EventArgs e)
        {
            base.OnPreInit(e);
            / / Determine whether the user is already logged in, if not, jump to the login interface
            if (!RedisSession.IsExistKey("UserCode"))
            {
                Response.Redirect("Login.aspx");
            }
        }
    }
}

 Let's take a look at how Default.aspx.cs uses RedisSession, so far we have achieved the same functions and usage as Asp.netSession.

RedisSession.Remove("UserCode");

   Compared with StateServer, RedisSession has the following advantages

   1. Redis server restart will not lose data 2. You can use redis' read-write separation cluster function to read and write data more efficiently  

   Test effect, use nginx and iis to deploy two sites for load balancing, iis1 address 127.0.0.1:8002 iis2 address 127.0.0.1:9000 nginx proxy service address 127.0.0.1:8003, if you don't know how to configure it, you can read my nginx +iis implements load balancing for this article. Let's take a look at the test results.

  To access 127.0.0.1:8003, you need to log in. The user name is admin and the password is 123

 

 After successful login, focus on the port number information

 

 Refresh the page, focusing on the port number information

You can try to directly access the iis1 address 127.0.0.1:8002 iis2 address 127.0.0.1:9000 These two sites, you will find that you do not need to log in. So far, our redis implementation of the session function is a success.

problem expansion

  The use of redis to realize the session comes to an end. Let's leave a question to discuss the solution. Wechat development provides many interfaces. Referring to the screenshot below, you can see that the access_token interface is called up to 2000 times a day. Now, many interfaces provided by large companies have different restrictions on the number of accesses to user interfaces of different levels. As for this restriction The reason should be to prevent malicious attacks and traffic restrictions and the like. So my question is how to implement the function of limiting the number of calls to this interface. You can use your imagination to participate in the discussion, maybe you will encounter this problem too.

 

  Here are the two options I know of:

     1. Using the token bucket algorithm in traffic shaping, a token bucket with a fixed size can generate tokens continuously at a constant rate by itself. If tokens are not consumed, or are consumed faster than they are produced, tokens will keep increasing until the bucket is filled. Subsequent tokens generated will overflow from the bucket. The maximum number of tokens that can be held in the final bucket will never exceed the size of the bucket.

  To put it simply: For example, in the access_token interface above, the frequency is 2000 times a day, that is, 1 time per minute. The capacity of our token bucket is 2000, which can be stored using the simplest key/value of redis, the key is the user id, and the value is the number of times the integer storage can be used, and then use a timer to call client.Incr(key) for 1 minute to achieve the number of times Self-incrementing; each time the user accesses the interface, the corresponding client.Decr(key) reduces the number of times of use.

  But there is a performance problem here. This is only for one user. Suppose there are 100,000 users. How to use a timer to implement this auto-increment operation? Is it to call client.Incr(key) in a loop 100,000 times? ? This has not been considered clearly.

      2. The total number of direct user accesses is judged first, and then an auto-increment is performed if the conditions are met.

      Comparison of advantages and disadvantages of the two schemes
  advantage shortcoming
Token Bucket Algorithm Precise flow control  The implementation is complicated, and due to the precise control, it is troublesome in practical applications. It is likely that the user accesses the interface less frequently from night to early morning, and more frequently during the day.
simple algorithm Simple and feasible implementation, high efficiency  Inaccurate flow control

Summarize

  This article explains redis from the practical application, and there should be a few more articles to introduce the practical application of redis, so stay tuned!

      The resource package download address used in this article: redis_demo

  svn download address: http://code.taobao.org/svn/ResidSessionDemo/

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326773240&siteId=291194637