Redis群集实现Asp.net Mvc分布式Session

Session的缺点

众所周知Asp.net Session默认存储在IIS中,IIS的重启会导致Session丢失。

如果你的网站使用了session,当网站并发过大时可能引起溢出。

 

配置Redis 集群

 


安装Redis

创建一个文件,进入该文件夹

下载Redis 地址:http://download.redis.io/releases/redis-3.0.4.tar.gz

tar -xvf redis-3.0.4.tar.gz 解压文件

cd redis-3.0.4 进入解压的文件

make && make install 安装

创建三个文件夹

把redis.conf文件分别复制到这三个文件夹

然后分别进入这三个文件夹修改配置文件和指定不同的端口

1.daemonize yes  (开启后台运行)

2.port 6379 (端口)

3.pidfile /var/run/redis-6379.pid (Redis 以守护进程的方式运行的时候,Redis 默认会把 pid 文件放在/var/run/redis.pid,你可以配置到其他地址。当运行多个 redis 服务时,需要指定不同的 pid 文件和端口)

4.cluster-enabled yes (是否启用集群)

5.cluster-config-file "nodes-6379.conf" (nodes 节点文件)

 

 安装ruby环境

yum -y install zlib ruby rubygems

gem install redis

 

开启服务

分别开启这三个实例

查看是否开启

 

 创建集群

首先,进入redis的安装包路径下

cd /usr/local/src/redis/redis-3.0.1/src/

执行命令:

./redis-trib.rb create --replicas 0 ./redis-trib.rb create --replicas 0 192.168.1.108:6379 192.168.1.108:6380 192.168.1.108:6381

进入redis-cli -p 6379 -c

Cluster nodes 查看集群信息

配置完成

 

 

 

使用 StackExchange.Redis驱动连接到redis


 

代码是我从Asp.net vnext Caching 程序集中copy下来的

配置类型

 public class RedisCacheOptions
        {
        /// <summary>
        /// 连接配置
        /// </summary>
        public string Configuration { get; set; }
       /// <summary>
       /// 实例名字
       /// </summary>
        public string InstanceName { get; set; }


        }
  static void Main(string[] args)
            {


            string key = "myKey";
            object state = null;
            string value1 = "yyyyyyyyyyyyyy";
            byte[] value = Encoding.Default.GetBytes(value1);
            Stream valueStream;

            Console.WriteLine("Connecting to cache");
            var cache = new RedisCache(new RedisCacheOptions
                {
                InstanceName = "sessionId",
                Configuration = "192.168.1.108:6379,192.168.1.108:6380,192.168.1.108:6381"
                });
            Console.WriteLine("Connected");

            Console.WriteLine("Setting");
            valueStream = cache.Set(key, state, context =>
            {
                context.Data.Write(value, 0, value.Length);
            });

RedisCache 集体实现

   public class RedisCache : IDistributedCache
        {
        //Lua 脚本
        private const string SetScript = (@" 
                redis.call('HMSET', KEYS[1], 'absexp', ARGV[1], 'sldexp', ARGV[2], 'data', ARGV[4])
                if ARGV[3] ~= '-1' then
                  redis.call('EXPIRE', KEYS[1], ARGV[3]) 
                end
                return 1");
        //key
        private const string AbsoluteExpirationKey = "absexp";
        //key
        private const string SlidingExpirationKey = "sldexp";
        //key
        private const string DataKey = "data";
        private const long NotPresent = -1;
        private ConnectionMultiplexer _connection;
        private IDatabase _cache;
        private readonly RedisCacheOptions _options;
        private readonly string _instance;
        /// <summary>
        /// 初始化配置
        /// </summary>
        /// <param name="optionsAccessor"></param>
        public RedisCache(RedisCacheOptions optionsAccessor)
            {
            _options = optionsAccessor;
            _instance = optionsAccessor.InstanceName;
            }
        public void Connect()
            {
            if (_connection == null)
                {
                _connection = ConnectionMultiplexer.Connect(_options.Configuration);
                _cache = _connection.GetDatabase();
                }
            }


        /// <summary>
        /// set到Redis中
        /// </summary>
        /// <param name="key"></param>
        /// <param name="state"></param>
        /// <param name="create"></param>
        /// <returns></returns>
        public Stream Set(string key, object state, Action<ICacheContext> create)
            {
            Connect();

            var context = new CacheContext(key) { State = state };
            //设置绝对过期时间
            //context.SetAbsoluteExpiration(DateTimeOffset.Now.AddMilliseconds(22222));
            //设置滑动过期时间
            context.SetSlidingExpiration(TimeSpan.FromSeconds(22222));
            create(context);
            var value = context.GetBytes();
            //Lua脚本赋值
            var result = _cache.ScriptEvaluate(SetScript, new RedisKey[] { _instance + key },
                new RedisValue[]
                {
                    context.AbsoluteExpiration?.Ticks ?? NotPresent,
                    context.SlidingExpiration?.Ticks ?? NotPresent,
                    context.GetExpirationInSeconds() ?? NotPresent,
                    value
                });
            return new MemoryStream(value, writable: false);
            }

        public bool TryGetValue(string key, out Stream value)
            {
            value = GetAndRefresh(key, getData: true);
            return value != null;
            }

        public void Refresh(string key)
            {
            var ignored = GetAndRefresh(key, getData: false);
            }

        private Stream GetAndRefresh(string key, bool getData)
            {
            Connect();


            RedisValue[] results;
            if (getData)
                {
                results = _cache.HashMemberGet(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey, DataKey);
                }
            else
                {
                results = _cache.HashMemberGet(_instance + key, AbsoluteExpirationKey, SlidingExpirationKey);
                }

            if (results.Length >= 2)
                {
                DateTimeOffset? absExpr;
                TimeSpan? sldExpr;
                MapMetadata(results, out absExpr, out sldExpr);
                Refresh(key, absExpr, sldExpr);
                }
            if (results.Length >= 3 && results[2].HasValue)
                {
                return new MemoryStream(results[2], writable: false);
                }
            return null;
            }
        private void MapMetadata(RedisValue[] results, out DateTimeOffset? absoluteExpiration, out TimeSpan? slidingExpiration)
            {
            absoluteExpiration = null;
            slidingExpiration = null;
            var absoluteExpirationTicks = (long?)results[0];
            if (absoluteExpirationTicks.HasValue && absoluteExpirationTicks.Value != NotPresent)
                {
                absoluteExpiration = new DateTimeOffset(absoluteExpirationTicks.Value, TimeSpan.Zero);
                }
            var slidingExpirationTicks = (long?)results[1];
            if (slidingExpirationTicks.HasValue && slidingExpirationTicks.Value != NotPresent)
                {
                slidingExpiration = new TimeSpan(slidingExpirationTicks.Value);
                }
            }

        /// <summary>
        /// 刷新缓存过期时间
        /// </summary>
        /// <param name="key"></param>
        /// <param name="absExpr"></param>
        /// <param name="sldExpr"></param>
        private void Refresh(string key, DateTimeOffset? absExpr, TimeSpan? sldExpr)
            {
            TimeSpan? expr = null;
            if (sldExpr.HasValue)
                {
                if (absExpr.HasValue)
                    {
                    var relExpr = absExpr.Value - DateTimeOffset.Now;
                    expr = relExpr <= sldExpr.Value ? relExpr : sldExpr;
                    }
                else
                    {
                    expr = sldExpr;
                    }
                _cache.KeyExpire(_instance + key, expr);
                // TODO: Error handling
                }
            }
        /// <summary>
        /// 移除指定key
        /// </summary>
        /// <param name="key"></param>
        public void Remove(string key)
            {
            Connect();
            _cache.KeyDelete(_instance + key);

            }
        }

运行结果

 

源码:http://pan.baidu.com/s/1gdm8F9h

 

posted @ 2015-10-14 17:34  欢呼雀跃  阅读(3959)  评论(6编辑  收藏  举报