`

分布式中使用Redis实现Session共享

 
阅读更多
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;
            //更新缓存过期时间
            RedisBase.Hash_SetExpire(SessionID, DateTime.Now.AddMinutes(Timeout));
        }

        /// <summary>
        /// SessionId标识符
        /// </summary>
        public static string SessionName = "Redis_SessionId";

        //
        // 摘要:
        //     获取会话状态集合中的项数。
        //
        // 返回结果:
        //     集合中的项数。
        public int Count
        {
            get
            {
                return RedisBase.Hash_GetCount(SessionID);
            }
        }

        //
        // 摘要:
        //     获取一个值,该值指示会话是否为只读。
        //
        // 返回结果:
        //     如果会话为只读,则为 true;否则为 false。
        public bool IsReadOnly { get; set; }

        //
        // 摘要:
        //     获取会话的唯一标识符。
        //
        // 返回结果:
        //     唯一会话标识符。
        public string SessionID
        {
            get
            {
                return GetSessionID();
            }
        }

        //
        // 摘要:
        //     获取并设置在会话状态提供程序终止会话之前各请求之间所允许的时间(以分钟为单位)。
        //
        // 返回结果:
        //     超时期限(以分钟为单位)。
        public int Timeout { get; set; }

        /// <summary>
        /// 获取SessionID
        /// </summary>
        /// <param name="key">SessionId标识符</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;
            }
        }

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

        // 摘要:
        //     判断会话中是否存在指定key
        //
        // 参数:
        //   name:
        //     键值
        //
        public bool IsExistKey(string name)
        {
            return RedisBase.Hash_Exist<object>(SessionID, name);
        }

        //
        // 摘要:
        //     向会话状态集合添加一个新项。
        //
        // 参数:
        //   name:
        //     要添加到会话状态集合的项的名称。
        //
        //   value:
        //     要添加到会话状态集合的项的值。
        public void Add(string name, object value)
        {
            RedisBase.Hash_Set<object>(SessionID, name, value);
        }
        //
        // 摘要:
        //     从会话状态集合中移除所有的键和值。
        public void Clear()
        {
            RedisBase.Hash_Remove(SessionID);
        }

        //
        // 摘要:
        //     删除会话状态集合中的项。
        //
        // 参数:
        //   name:
        //     要从会话状态集合中删除的项的名称。
        public void Remove(string name)
        {
            RedisBase.Hash_Remove(SessionID,name);
        }
        //
        // 摘要:
        //     从会话状态集合中移除所有的键和值。
        public void RemoveAll()
        {
            Clear();
        }
    }
}

 下面是实现类似在cs文件中能直接使用Session["UserId"]的方式,我的MyPage类继承Page实现了自己的逻辑主要做了两件事  1:初始化RedisSession  2:实现统一登录认证,OnPreInit方法里面判断用户是否登录,如果没有登录了则跳转到登陆界面

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

namespace ResidSessionDemo.RedisDemo
{
    /// <summary>
    /// 自定义Page 实现以下功能
    /// 1.初始化RedisSession
    /// 2.实现页面登录验证,继承此类,则可以实现所有页面的登录验证
    /// </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);
            //判断用户是否已经登录,如果未登录,则跳转到登录界面
            if (!RedisSession.IsExistKey("UserCode"))
            {
                Response.Redirect("Login.aspx");
            }
        }
    }
}

 我们来看看Default.aspx.cs是如何使用RedisSession的,至此我们实现了和Asp.netSession一模一样的功能和使用方式。

RedisSession.Remove("UserCode");

   相比StateServer,RedisSession具有以下优点

   1.redis服务器重启不会丢失数据  2.可以使用redis的读写分离个集群功能更加高效读写数据  

   测试效果,使用nginx和iis部署两个站点做负载均衡,iis1地址127.0.0.1:8002 iis2地址127.0.0.1:9000  nginx代理服务地址127.0.0.1:8003,不懂如何配置的可以去阅读我的nginx+iis实现负载均衡这篇文章。我们来看一下测试结果。

  访问127.0.0.1:8003 需要进行登录   用户名为admin  密码为123

 

 登录成功以后,重点关注端口号信息

 

 刷新页面,重点关注端口号信息

可以尝试直接访问iis1地址127.0.0.1:8002 iis2地址127.0.0.1:9000 这两个站点,你会发现都不需要登录了。至此我们的redis实现session功能算是大功告成了。

问题拓展

  使用redis实现session告一段落,下面留个问题讨论一下方案。微信开发提供了很多接口,参考下面截图,可以看到获取 access_token接口每日最多调用2000次,现在大公司提供的很多接口针对不对级别的用户接口访问次数限制都是不一样的,至于做这个限制的原因 应该是防止恶意攻击和流量限制之类的。那么我的问题是怎么实现这个接口调用次数限制功能。大家可以发挥想象力参与讨论哦,或许你也会碰到这个问题。

 

  先说下我知道的两种方案:

     1.使用流量整形中的令牌桶算法,大小固定的令牌桶可自行以恒定的速率源源不断地产生令牌。如果令牌不被消耗,或者被消耗的速度小于产生 的速度,令牌就会不断地增多,直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小。

  说浅显点:比如上面的获取access_token接口,一天2000次的频率,即1次/分钟。我们令牌桶容量为2000,可以使用redis 最简单的key/value来存储 ,key为用户id,value为整形存储还可使用次数,然后使用一个定时器1分钟调用client.Incr(key) 实现次数自增;用户每访问一次该接口,相应的client.Decr(key)来减少使用次数。

  但是这里存在一个性能问题,这仅仅是针对一个用户来说,假设有10万个用户,怎么使用定时器来实现这个自增操作呢,难道是循环10万次分别调用client.Incr(key)吗?这一点没有考虑清楚。

      2.直接用户访问一次 先进行总次数判断,符合条件再就进行一次自增

      两种方案优缺点比较
  优点 缺点
令牌桶算法 流量控制精确  实现复杂,并且由于控制精确反而在实际应用中有麻烦,很可能用户在晚上到凌晨期间访问接口次数不多,白天访问次数多些。
简单算法 实现简单可行,效率高  流量控制不精确

总结

  本篇从实际应用讲解了redis,后面应该还会有几篇继续介绍redis实际应用,敬请期待!

      本篇文章用到的资源打包下载地址:redis_demo

  svn下载地址:http://code.taobao.org/svn/ResidSessionDemo/

分享到:
评论

相关推荐

    分布式中使用Redis实现Session共享(下)共4页

    在分布式系统中,Session共享是实现用户会话跨服务器持久化的重要技术,这对于提供高可用性和可扩展性服务至关重要。...以上是关于分布式环境中使用Redis实现Session共享的详细知识,希望对你理解这一主题有所帮助。

    分布式中使用Redis实现Session共享(上)共11页.pdf.zip

    本文将详细探讨如何在分布式环境中使用Redis实现Session共享。 首先,理解Session的基本概念至关重要。Session是Web服务器用于跟踪用户状态的一种机制,当用户登录后,服务器会为该用户创建一个唯一的Session ID,...

    分布式中使用Redis实现Session共享(下)共4页.pdf.zip

    综上所述,通过将Session数据存储在Redis中,分布式系统可以实现跨服务的Session共享,从而提高用户体验并简化开发工作。不过,实施过程中需要注意数据序列化、并发控制、扩展性和安全性等问题,确保系统的可靠性和...

    springboot +shiro+redis实现session共享(方案二)1

    本文档旨在介绍如何使用 Spring Boot、Shiro 和 Redis 实现分布式 session 共享,以解决 Web 应用程序的登录 session 统一问题。 2. 相关依赖 在实现 session 共享之前,需要在项目中引入相关依赖项,包括: * ...

    ssm+redis 实现session共享

    SSM+Redis 实现Session共享是现代Web应用中常见的技术组合,主要目的是在分布式系统中保持用户Session的一致性。SSM是指Spring、Spring MVC和MyBatis这三大Java Web开发框架的组合,而Redis则是一种高性能的键值存储...

    Tomcat8(Tomcat9)+redis实现Session共享(支持Redis集群)

    总之,通过Tomcat和Redis结合实现Session共享,不仅解决了分布式环境下的会话管理问题,还利用了Redis的高效特性,提升了系统的整体性能。这个方案对于大型、高并发的Web应用来说,是一种有效的优化策略。

    springboot + redis实现session共享

    本文将详细解释如何使用Spring Boot和Redis来实现session共享,并探讨相关的核心概念和技术。 首先,我们要了解什么是session。在Web应用程序中,session是一种用于跟踪用户状态的技术。当用户登录后,服务器会为该...

    集群redis实现session共享jar包之tomcat8

    总结,通过在Tomcat 8中集成Redis,我们可以实现高效的session共享,从而在分布式环境中提供一致的用户体验。这是一项重要的技术实践,对于提升大型Web应用的稳定性和可靠性具有重要意义。在实际操作中,应根据具体...

    分布式集群Session共享 简单多tomcat8+redis的session共享实现

    首先,让我们来看看如何在Tomcat 8中实现基于Redis的Session共享。`context.xml`文件是Tomcat配置的一部分,它定义了每个Web应用的上下文环境。在这个场景下,我们需要在`context.xml`中配置一个名为`Manager`的元素...

    利用redis实现session共享

    本篇文章将深入探讨如何利用Redis实现Session共享,同时结合Nginx的负载均衡策略,以及Spring Boot的应用框架,构建一个高效、可靠的分布式系统。 一、Session共享的挑战与解决方案 在传统的单体应用中,Session...

    tomcat8集群redis实现session共享jar包

    首先,让我们了解为什么要在Tomcat集群中使用Redis进行会话共享。在没有会话共享的情况下,当用户从一个服务器跳转到另一个服务器时,他们的会话信息(如购物车、登录状态等)可能无法保留,因为每个服务器都有自己...

    集群redis实现session共享jar包之tomcat7

    在分布式系统中,Session共享是实现用户会话一致性的重要手段,特别是在集群环境下。"集群redis实现session共享jar包之tomcat7"这个主题涉及到的是如何在基于Tomcat 7的Web应用集群中,利用Redis作为中央存储来共享...

    nginx+redis实现session共享

    在IT行业中,构建高效、可扩展的Web服务是至关重要的,而"nginx+redis实现session共享"的主题就涉及到了这一核心需求。为了实现单点登录(Single Sign-On, SSO)以及跨应用的session共享,我们可以结合使用Nginx作为...

    tomcat8-redis实现session共享jar包

    标题中的“tomcat8-redis实现session共享jar包”指的是在Tomcat 8这个流行的Java应用服务器中,通过集成Redis缓存系统来实现session数据的跨服务器共享。这是一个常见的优化策略,尤其在分布式环境中,确保用户在...

    SpringSession+Redis实现Session共享案例

    下面将详细解释`SpringSession`和`Redis`在实现Session共享中的关键知识点。 1. **SpringSession**: - `SpringSession` 是Spring生态系统的一个扩展,它允许我们将HTTP Session数据存储在外部存储(如Redis)中,...

    tomcat8-redis-session共享

    标题 "tomcat8-redis-session共享" 涉及到的是在Tomcat 8中使用Redis作为Session共享存储的解决方案。这是一个常见的需求,特别是在分布式系统中,为了保持用户会话的一致性,需要将Session数据在多台服务器之间共享...

    tomcat8集群与redis实现session共享所需Jar包

    标题"tomcat8集群与redis实现session共享所需Jar包"指的是将Redis作为中央存储来保存Tomcat集群中的session数据。Redis因其高性能、轻量级以及丰富的数据结构支持,常被选作session共享的中间件。描述中的"本人已经...

    Spring Session + redis实现session共享

    在现代Web应用开发中,session共享是一个至关重要的需求,特别是在分布式系统中,多个服务器节点需要共享用户的状态信息。Spring Session + Redis的结合提供了一个高效且可靠的解决方案,它允许跨服务器节点透明地...

    tomcat基于redis实现session共享所依赖的jar包

    "tomcat基于redis实现session共享所依赖的jar包"就是解决这个问题的关键组件。 首先,我们需要理解session共享的基本概念。Session是Web服务器为特定用户浏览器创建的一种持久化的数据存储方式,用于存储用户在网站...

Global site tag (gtag.js) - Google Analytics