c# ASP.NET 关于分布式Session的实践--架构笔记

17 篇文章 0 订阅
5 篇文章 0 订阅
2017-10-18更新
新增:
1)使用者强制制定session的key值,有重复指定危险。慎用,适用于短信验证码、图形验证码等功能。
2)增加微信公众账号支持。
由于公司域名全部要加CDN,原来的运维是把N台机器的Session写到一台机器上。所以用C#原生的Session不会产生任何问题。
由于业务上用session的地方较多,想重写原Session。
把Session存储在Memcached或Redis中。
先了解下ASP.NET——SessionID
知识点:
       HTTP 是一种无状态协议。这意味着 Web 服务器会将针对页面的每个 HTTP 请求作为独立的请求进行处理。服务器不会保留以前的请求过程中所使用的变量值的任何信息。
ASP.NET通过会话标识符来判断是否是同一个客户端。简单点说就是依赖ASP.NET的SessionID值来判断,而默认情况下, SessionID 值存储在 Cookie 中。
Session是依赖于cookie的
如图下图:
会话标识符
会话由一个唯一标识符标识,可使用 SessionID 属性读取此标识符。为 ASP.NET 应用程序启用会话状态时,将检查应用程序中每个页面请求是否有浏览器发送的 SessionID 值。如果未提供任何 SessionID 值,则 ASP.NET 将启动一个新会话,并将该会话的 SessionID 值随响应一起发送到浏览器。
默认情况下,SessionID 值存储在 Cookie 中。但也可以将应用程序配置为在“无 Cookie”会话的 URL 中存储 SessionID 值。
只要一直使用相同的 SessionID 值来发送请求,会话就被视为活动的。如果特定会话的请求间隔超过指定的超时值(以分钟为单位),则该会话被视为已过期。使用过期的 SessionID 值发送的请求将生成一个新的会话。
ASP.NET客户端与服务端一般是2种情景
情景一:客服端Cookie中无ASP.NET SessionID。ASP.NET服务端会检测客户端ASP.NET SessionID是否有值,如没有,服务端会回写客户端的ASP.NET SessionID。(注意:ASP.NET的SessionID是只读的,Cookie中默认的ASP.NET_SessionID为永不过期。)
情景二:ASP.NET 客户端每次访问服务端,会带着Cookie中ASP.NET SessionID值,跟服务端交互。当客户端中如有ASP.NET SessionID值,那么服务端的SessionID值就是跟客户端的一致。
如图所示:

分布式Session改造
1、原来的IIS中的Session使用集群的Redis或MemCached替代。
2、替代原则客户端通过cookie存一个唯一标识的值。类似于上图ASP.NET_SessionID.
3、利用cookie建立对应关系,实现Session的存取。实现存取时原Session的KEY值+cookie中唯一标识,对应Cache中的KEY。
如情景①中需要使用session["UserID"]=18    Cache中对应key如下kutroxgrm2eslsmdwz2nemvbfSL516+UserID=18  

知识点:
       HTTP 是一种无状态协议。这意味着 Web 服务器会将针对页面的每个 HTTP 请求作为独立的请求进行处理。服务器不会保留以前的请求过程中所使用的变量值的任何信息。
ASP.NET通过会话标识符来判断是否是同一个客户端。简单点说就是依赖ASP.NET的SessionID值来判断,而默认情况下, SessionID 值存储在 Cookie 中。
Session是依赖于cookie的
如图下图:
会话标识符
会话由一个唯一标识符标识,可使用 SessionID 属性读取此标识符。为 ASP.NET 应用程序启用会话状态时,将检查应用程序中每个页面请求是否有浏览器发送的 SessionID 值。如果未提供任何 SessionID 值,则 ASP.NET 将启动一个新会话,并将该会话的 SessionID 值随响应一起发送到浏览器。
默认情况下,SessionID 值存储在 Cookie 中。但也可以将应用程序配置为在“无 Cookie”会话的 URL 中存储 SessionID 值。
只要一直使用相同的 SessionID 值来发送请求,会话就被视为活动的。如果特定会话的请求间隔超过指定的超时值(以分钟为单位),则该会话被视为已过期。使用过期的 SessionID 值发送的请求将生成一个新的会话。
在了解其本质后,改造现有的Session,经过筛选实现下图功能。(选择最常用的功能。红框)


核心代码如下:
运行不了的,请屏蔽用,自行补全Memcached的存取。

 public sealed class Session
    {
        #region
        private System.Web.HttpContext current = null;//System.Web.HttpContext.Current;
        private MemcachedContainer _cacheContainer = new MemcachedContainer();
        private readonly string company = "CCTV_Session_";
        private readonly string sessionId = "CCTV_SessionId"; //"ASP.NET_SessionId";//  //"ASP.NET_SessionId"
        private readonly string opengid = "openid";
        public bool IsDebug = false;
        public bool IsCustomKey = false;
        #endregion
        public Session()
        {
            current = System.Web.HttpContext.Current;
        }
        public Session(bool isDebug)
        {
            IsDebug = isDebug;
            current = System.Web.HttpContext.Current;
        }

        public Session(int isCustomKey)
        {
            IsCustomKey = Convert.ToBoolean(isCustomKey);
            current = System.Web.HttpContext.Current;
        }
        //public HttpSessionState Session;
        //public int Timeout = 20 * 1000;
        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeOut"></param>
        public void Add(string key, object value, int timeOut = 20*60)
        {
            // memcachedClient.get (current.Session.SessionID + key); 

            if (current == null)
            {
                if (IsDebug) Logs.WriteLogInfo("[Add]SessionID=current为null", "debug");
                _cacheContainer.Add(company + key, value, timeOut);
            }
            else
            {
                string sessionValue = null;
                if (IsCustomKey) //用户强制指定key值有重复危险慎用
                {
                    if (IsDebug) Logs.WriteLogInfo("[当前创建-用户自定义]SessionID=" + sessionValue + "【完整版=】" + company + sessionValue + "_" + key + "【值:】" + value, "debug");
                }
                else if (!current.Request.Cookies.AllKeys.Contains(sessionId))//如果客户端不包含Session值那么创建一个SessionID
                {
                    sessionValue = System.Guid.NewGuid().ToString("N");
                    current.Response.Cookies.Add(new HttpCookie(sessionId, sessionValue));//服务端返回

                    if (IsDebug) Logs.WriteLogInfo("[当前创建]SessionID=" + sessionValue + "【完整版=】" + company + sessionValue + "_" + key + "【值:】" + value, "debug");
                }
                else if (current.Request.Params.AllKeys.Contains(opengid))//添加微信公众号支持
                {
                    sessionValue = current.Request.Params[opengid];
                    if (IsDebug) Logs.WriteLogInfo("[当前创建]opengid=" + sessionValue + "【完整版=】" + company + sessionValue + "_" + key + "【值:】" + value, "debug");
                }
                else
                {
                    sessionValue = current.Request.Cookies[sessionId].Value;
                    if (IsDebug) Logs.WriteLogInfo("[客户端传来]SessionID=" + sessionValue + "[完整版=]" + company + sessionValue + "_" + key + "【值:】" + value, "debug");
                }
                _cacheContainer.Add(company + sessionValue + "_" + key, value, timeOut);
            }
        }

        /// <summary>
        /// 获取key值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public object Get(string key)
        {
            try
            {

                if (current == null)
                {
                    if (IsDebug) Logs.WriteLogInfo("[获取当前]SessionID=current为null", "debug");
                    return _cacheContainer.Get(company + key);
                }
                else
                {
                    string sessionValue = "";
                    if (IsCustomKey) //用户强制指定key值有重复危险慎用
                    {
                        if (IsDebug) Logs.WriteLogInfo("[当前创建-用户自定义]SessionID=" + sessionValue + "[完整版=]" + company + sessionValue + "_" + key + "[值:]" + _cacheContainer.Get(company + sessionValue + "_" + key), "debug");

                    }
                    else if (current.Request.Cookies.AllKeys.Contains(sessionId))
                    {
                        sessionValue = current.Request.Cookies[sessionId].Value;
                        if (IsDebug) Logs.WriteLogInfo("[获取当前]SessionID=" + sessionValue + "[完整版=]" + company + sessionValue + "_" + key + "[值:]" + _cacheContainer.Get(company + sessionValue + "_" + key), "debug");

                    }
                    else if (current.Request.Params.AllKeys.Contains(opengid))//添加微信公众号支持
                    {
                        sessionValue = current.Request.Params[opengid];
                        if (IsDebug) Logs.WriteLogInfo("[获取当前]opengid=" + sessionValue + "[完整版=]" + company + sessionValue + "_" + key + "[值:]" + _cacheContainer.Get(company + sessionValue + "_" + key), "debug");
                    }
                    return _cacheContainer.Get(company + sessionValue + "_" + key);
                }
            }
            catch (Exception ex)
            {

                return null;
            }


        }
        /// <summary>
        /// 移除
        /// </summary>
        /// <param name="name"></param>
        public bool Remove(string name)
        {
            try
            {
                if (current == null)
                {
                    if (IsDebug) Logs.WriteLogInfo("[移除当前]SessionID=current为null", "debug");
                    _cacheContainer.Remove(company + name);
                    return false;
                }
                else
                {
                    string sessionValue = "";

                    if (IsCustomKey) //用户强制指定key值有重复危险慎用
                    {
                        if (IsDebug) Logs.WriteLogInfo("[移除当前]SessionID=" + sessionValue + "【完整版=】" + company + sessionValue + "_" + name, "debug");
                    }
                    else if (current.Request.Cookies.AllKeys.Contains(sessionId))
                    {
                        sessionValue = current.Request.Cookies[sessionId].Value;
                        if (IsDebug) Logs.WriteLogInfo("[移除当前]SessionID=" + sessionValue + "【完整版=】" + company + sessionValue + "_" + name, "debug");
                    }
                    else if (current.Request.Params.AllKeys.Contains(opengid))//添加微信公众号支持
                    {
                        sessionValue = current.Request.Params[opengid];
                        if (IsDebug) Logs.WriteLogInfo("[移除当前]opengid=" + sessionValue + "【完整版=】" + company + sessionValue + "_" + name, "debug");

                    }
                    _cacheContainer.Remove(company + sessionValue + "_" + name);
                    return true;
                }
            }
            catch (Exception ex)
            {

                return false;
            }

        }

        //
        // 摘要: 
        //     按名称获取或设置会话值。
        //
        // 参数: 
        //   name:
        //     会话值的键名。
        //
        // 返回结果: 
        //     具有指定名称的会话状态值;如果该项不存在,则为 null。
        public object this[string name]
        {
            get { return Get(name); }
            set
            {
                Add(name, value);
            }
        }

    }


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值