当前位置: 移动技术网 > IT编程>数据库>Redis > 详解用Redis实现Session功能

详解用Redis实现Session功能

2017年12月08日  | 移动技术网IT编程  | 我要评论
0.什么是redis redis是一个开源的使用ansi c语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的api 1.

0.什么是redis

redis是一个开源的使用ansi c语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的api

1.与其他用户状态保存方案比较

一般开发中用户状态使用session或者cookie,两种方式各种利弊。

session:在inproc模式下容易丢失,并且引起并发问题。如果使用sqlserver或者sqlserver模式又消耗了性能

cookie则容易将一些用户信息暴露,加解密同样也消耗了性能。

redis采用这样的方案解决了几个问题,

①.redis存取速度快。

②.用户数据不容易丢失。

③.用户多的情况下容易支持集群。

④.能够查看在线用户。

⑤.能够实现用户一处登录。(通过代码实现,后续介绍)

⑥.支持持久化。(当然可能没什么用)

2.实现思路

1.我们知道session其实是在cookie中保存了一个sessionid,用户每次访问都将sessionid发给服务器,服务器通过id查找用户对应的状态数据。

在这里我的处理方式也是在cookie中定义一个sessionid,程序需要取得用户状态时将sessionid做为key在redis中查找。

2.同时session支持用户在一定时间不访问将session回收。

借用redis中keys支持过期时间的特性支持这个功能,但是在续期方面需要程序自行拦截请求调用这个方法(demo有例子)

下面开始代码说明 

3.redis调用接口

首先引用servicestack相关dll。

在web.config添加配置,这个配置用来设置redis调用地址每台服务用【,】隔开。主机写在第一位 

 <appsettings> 
  <!--每台redis之间用,分割.第一个必须为主机-->
  <add key="sessionredis" value="127.0.0.1:6384,127.0.0.1:6384"/> 
 </appsettings>

初始化配置

static managers()
    {
      string sessionredis= configurationmanager.appsettings["sessionredis"];
      string timeout = configurationmanager.appsettings["sessionredistimeout"];

      if (string.isnullorempty(sessionredis))
      {
        throw new exception("web.config 缺少配置sessionredis,每台redis之间用,分割.第一个必须为主机");
      }

      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,//“写”链接池链接数
        maxreadpoolsize = writereadcount,//“读”链接池链接数
        autostart = true
      });
    }

 为了控制方便写了一个委托

 /// <summary>
    /// 写入
    /// </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写入异常.host:" + client.host + ",port:" + client.port);
      }
      finally
      {
        if (client != null)
        {
          client.dispose();
        }
      }
    }

 一个调用的例子其他的具体看源码

   /// <summary>
    /// 以key/value的形式存储对象到缓存中
    /// </summary>
    /// <typeparam name="t">对象类别</typeparam>
    /// <param name="value">要写入的集合</param>
    public void kset(dictionary<string, t> value)
    {
      func<iredisclient, bool> fun = (iredisclient client) =>
      {
        client.setall<t>(value);
        return true;
      };

      tryrediswrite(fun);
    }

4.实现session

按上面说的给cookie写一个sessionid

  /// <summary>
  /// 用户状态管理
  /// </summary>
  public class session
  {
    /// <summary>
    /// 初始化
    /// </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;
      }
    }

  }

去存取用户的方法

    /// <summary>
    /// 获取当前用户信息
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <returns></returns>
    public object get<t>() where t:class,new()
    {
      return new redisclient<t>().kget(sessionid);
    }

    /// <summary>
    /// 用户是否在线
    /// </summary>
    /// <returns></returns>
    public bool islogin()
    {
      return new redisclient<object>().kisexist(sessionid);
    }

    /// <summary>
    /// 登录
    /// </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));
    }
 

 6.续期

默认用户没访问超过30分钟注销用户的登录状态,所以用户每次访问都要将用户的注销时间推迟30分钟

这需要调用redis的续期方法 

 

    /// <summary>
    /// 延期
    /// </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);
    }

 封装以后
/// <summary>
/// 续期
/// </summary>
public void postpone()
{
new redisclient<object>().ksetentryin(sessionid, new timespan(0, managers.timeout, 0));
}

这里我利用了mvc3中的actionfilter,拦截用户的所有请求

namespace test
{
  public class sessionfilterattribute : actionfilterattribute
  {
    /// <summary>
    /// 每次请求都续期
    /// </summary>
    /// <param name="filtercontext"></param>
    public override void onactionexecuting(actionexecutingcontext filtercontext)
    {
      new session(filtercontext.httpcontext).postpone();
    }
  }
}

在global.asax中要注册一下

public static void registerglobalfilters(globalfiltercollection filters)
    {
      filters.add(new sessionfilterattribute());
    }

    protected void application_start()
    {
      registerglobalfilters(globalfilters.filters);
    } 

 5.调用方式

为了方便调用借用4.0中的新特性,把controller添加一个扩展属性 

public static class extsessions
{public static session sessionext(this controller controller)
  {
    return new session(controller.httpcontext);
  }
}

调用方法

  public class homecontroller : controller
  {
    public actionresult index()
    {
      this.sessionext().islogin();
      return view();
    }
  }

6.代码下载

7.后续

sessionmanager包含 获取用户列表数量,注销某个用户,根据用户id获取用户信息,在线用户对象列表,在线用户sessionid列表等方法

后续将实现用户一处登录功能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网