Redis分布式队列和缓存更新

  原文链接:https://www.cnblogs.com/hua66/p/9600085.html

  在使用Redis中,我们可能会遇到以下场景:

  例如:

  某用户向服务器中发送一个请求,服务器将用户请求加入Redis任务队列,任务完成则移出队列。

  以上场景有几点疑问:

  1. Redis队列中数据如果不仅仅来自于我们的应用程序,那么我们怎么把这个数据加入Redis?
  2. 当Redis队列中用户的请求达程序所能处理的峰值。那么我们该如何处理这些用户请求?

  解决方案:

  1. 对外提供接口,将请求数据添加至DB。启动一个定时服务,在规定时间扫描DB中的请求数据并添加至Redis队列。
  2. 使用分布式异步队列。

  以上解决方案都可以使用插件来实现。

  1. 使用Quartz.Net
  2. 使用StackExchange.Redis

  一、

  关于Quartz.Net可以通过上面链接获取官方API。它与SQL Server中的代理作业有着同样功能。

  代码示例:

  

 1  /// <summary>
 2         /// 添加Job并以周期的形式运行
 3         /// </summary>
 4         /// <typeparam name="T"></typeparam>
 5         /// <param name="jobName">job名称</param>
 6         /// <param name="jobGroupName">job组名称</param>
 7         /// <param name="replace">job是否可修改</param>
 8         /// <param name="triggerName">job触发器的名称</param>
 9         /// <param name="minutes">job执行的时间间隔,以分为单位</param>
10         /// <returns></returns>
11         public DateTimeOffset AddJob<T>(string jobName, string jobGroupName, bool replace, string triggerName, int minutes) where T : IJob
12         {
13             IJobDetail jobDetail = JobBuilder.Create<T>().WithIdentity(jobName, jobGroupName).Build();
14             _sched.AddJob(jobDetail, replace);
15             ITrigger trigger = TriggerBuilder.Create()
16               .WithIdentity(triggerName, jobGroupName)
17               .StartNow()
18               .WithSimpleSchedule(x => x
19                   .WithIntervalInMinutes(minutes)//表示分钟的时间间隔
20                   .RepeatForever())
21               .Build();
22             return _sched.ScheduleJob(jobDetail, trigger).Result;
23         }

  以上的代码中是基于Quartz封装一个添加了Job的方法。这个方法依赖于 “IJobDetail” 和 “ITrigger” 这两个对象。

  “IJobDetail” 表示Job的身份信息,“ITrigger” 则包含了Job执行信息,它表示Job该如何执行。

  以下为调用示例:

  

1 QuartzHelper quartzHelper = QuartzHelper.CreateInstance();
2                 quartzHelper.AddJob<TestJob>("testJob", "testJob_Group",false, "testJob_Trigger",1*10);

  上述实例中的 “TestJob” 实现Quartz.Net中的 "IJob" 接口。这个接口只有一个方法 “Execute”,并由Quartz.Net框架自行调用该方法。

  你可以在此方法中执行你的代码。并在添加该Job制定你的执行策略 “ITrigger” 对象。然后框架会根据你制定的策略进行调用。调用参数请参见上述封装。

  下面是向Redis队列插入数据的示例Job:

  

 1 public class TestJob : IJob
 2     {
 3         Task IJob.Execute(IJobExecutionContext context)
 4         {
 5             //JobDataMap dataMap = context.JobDetail.JobDataMap;
 6             Task task = Task.Run(
 7                   () =>
 8                   {
 9                       Console.WriteLine(string.Format("{0}开始执行!当前系统时间{1}", this.GetType().Name, DateTime.Now));
10                       try
11                       {
12                           string redisKey = this.GetType().Name;
13                           RedisHelper redisHelper = new RedisHelper();
14                           if (redisHelper.KeyExists(redisKey))
15                           {
16                               redisHelper.KeyDelete(redisKey);
17                           };
18
19                           for (int i = 1; i <= 10; i++)
20                           {
21                               User user = new User()
22                               {
23                                   ID = i,
24                                   Name = "user" + DateTime.Now.Ticks +"_"+ i
25                               };
26                               redisHelper.ListLeftPush<User>(redisKey, user);//模拟DB用户数据
27                           }
28                       }
29                       catch (Exception ex)
30                       {
31                           Console.WriteLine(string.Format("{0}任务出现异常,异常信息:{1}!当前系统时间{2}", this.GetType().Name, ex.Message, DateTime.Now));
32                       }
33                   }
34                   );
35             return task;
36         }
37     }

  上面的 “TestJob” 模拟了从DB加载用户请求数据至Redis队列。至此我们已经解决了上面的第一个问题。

  二、

  在.Net中Redis的插件不多。比较流行有 "ServiceStack.Redis" 和 "StackExchange.Redis" 。

  "ServiceStack.Redis" 为官方推出的插件。非开源插件,且普通版最高只支持 6000/S 读写。高级版是要收费的。为了后续扩展,这里我们采用 "StackExchange.Redis" 。

  关于StackExchange.Redis可以通过上面链接获取官方API,目前是开源的。

  在第一个问题中,已经通过定时Job的方式向Redis队列中填充数据。下面我们通过 "StackExchange.Redis" 获取Redis队列中的请求并处理这些请求。

  1.加载数据至Redis:

  

 1 using APP_Test.Job;
 2 using Common.Quartz;
 3 using Common.Redis.StackExchange;
 4 using Quartz;
 5 using System;
 6 using System.Collections.Generic;
 7 using System.Linq;
 8 using System.Text;
 9 using System.Threading.Tasks;
10
11 namespace APP_Test
12 {
13     class Program
14     {
15         static void Main(string[] args)
16         {
17             {
18                 RedisHelper redisHelperA = new RedisHelper();
19                 RedisHelper redisHelperB = new RedisHelper();
20                 string stra = redisHelperA.StringGet("mykey");
21                 string strb = redisHelperB.StringGet("mykey");
22                 if (stra== strb)
23                 {
24                     Console.WriteLine(string.Format("***********{0}=={1}***********", stra, strb));
25                 }
26                 else
27                 {
28                     Console.WriteLine(string.Format("***********{0}!={1}***********", stra, strb));
29                 }
30             }
31
32             {
33
34                 QuartzHelper quartzHelper = QuartzHelper.CreateInstance();
35                 quartzHelper.AddJob<TestJob>("testJob", "testJob_Group",false, "testJob_Trigger",1*10);//这里设置了以秒为单位
36             }
37
38             Console.ReadKey();
39         }
40
41     }
42 }

  

  可以看到上面代码执行的时间节点与我们所添加job中的 ”ITrigger “ 的触发策略完全一致。至此,我们第一步已得到验证。

  2.启动处理Redis队列中请求的程序。

  

 1 using APP_Test.Models;
 2 using Common.Redis.StackExchange;
 3 using System;
 4 using System.Collections.Generic;
 5 using System.Linq;
 6 using System.Text;
 7 using System.Threading.Tasks;
 8
 9 namespace APP_RedisClientTest
10 {
11     class Program
12     {
13         static void Main(string[] args)
14         {
15             RedisHelper redisHelper = new RedisHelper();
16             string redisKey = "TestJob";
17             while (true)
18             {
19                 Action action = new Action(() =>
20                 {
21                     User user = redisHelper.ListLeftPop<User>(redisKey);//获取请求数据并移出队列
22                     if (user!=null)
23                     {
24                         Console.WriteLine(string.Format("*******{0}*******", user.Name));
25                     }
26                 }
27                     );
28                 action.EndInvoke(action.BeginInvoke(null, null));
29             }
30             Console.ReadKey();
31         }
32     }
33 }

  

  上面我启动3个客户端实例,他们一起处理Redis队列中的请求。每当Job向Redis队列中添加请求对象后就会立即被我们处理请求的程序获取并消费,每当一个请求被消费就会被移出Redis队列。并且遵循先入先出的准则。按照上述,如果出现主程序请求量过高情形,我们只需要启动多个处理请求的辅助程序即可缓解主程序的压力。

  至此上面的两个问题已得到验证。

  如下附个人基于 "Quartz.Net" 和 "StackExchange.Redis" 封装的帮助类

  

  1 using System;
  2 using System.Collections.Generic;
  3
  4 namespace Common.Quartz
  5 {
  6     using global::Quartz;
  7     using global::Quartz.Impl;
  8     using global::Quartz.Impl.Matchers;
  9
 10     /// <summary>
 11     /// V:3.0.6.0
 12     /// </summary>
 13     public class QuartzHelper
 14     {
 15         private readonly static object _obj = new object();//单例锁
 16
 17         //private  ISchedulerFactory _sf = null;
 18
 19         private static IScheduler _sched = null;
 20         /// <summary>
 21         /// 提供IScheduler对象,访问异步方法
 22         /// </summary>
 23         public IScheduler Scheduler { get { return _sched; } }
 24
 25         private static QuartzHelper _quartzHelper = null;//单例对象
 26
 27         private QuartzHelper()
 28         {
 29             //_sf = new StdSchedulerFactory();
 30             //_sched = _sf.GetScheduler().Result;
 31             _sched = StdSchedulerFactory.GetDefaultScheduler().Result;
 32             _sched.Start();
 33         }
 34
 35         /// <summary>
 36         /// 获取单例对象
 37         /// </summary>
 38         /// <returns></returns>
 39         public static QuartzHelper CreateInstance()
 40         {
 41             if (_quartzHelper == null) //双if +lock
 42             {
 43                 lock (_obj)
 44                 {
 45                     if (_quartzHelper == null)
 46                     {
 47                         _quartzHelper = new QuartzHelper();
 48                     }
 49                 }
 50             }
 51             return _quartzHelper;
 52         }
 53         public bool CheckExists(TriggerKey triggerKey)
 54         {
 55             return _sched.CheckExists(triggerKey).Result;
 56         }
 57         public bool CheckExists(JobKey jobKey)
 58         {
 59             return _sched.CheckExists(jobKey).Result;
 60         }
 61         public IReadOnlyCollection<IJobExecutionContext> GetCurrentlyExecutingJobs()
 62         {
 63             return _sched.GetCurrentlyExecutingJobs().Result;
 64         }
 65
 66         /// <summary>
 67         /// 添加Job并以周期的形式运行
 68         /// </summary>
 69         /// <typeparam name="T"></typeparam>
 70         /// <param name="jobName">job名称</param>
 71         /// <param name="jobGroupName">job组名称</param>
 72         /// <param name="replace">job是否可修改</param>
 73         /// <param name="triggerName">job触发器的名称</param>
 74         /// <param name="minutes">job执行的时间间隔,以分为单位</param>
 75         /// <returns></returns>
 76         public DateTimeOffset AddJob<T>(string jobName, string jobGroupName, bool replace, string triggerName, int minutes) where T : IJob
 77         {
 78             IJobDetail jobDetail = JobBuilder.Create<T>().WithIdentity(jobName, jobGroupName).Build();
 79             _sched.AddJob(jobDetail, replace);
 80             ITrigger trigger = TriggerBuilder.Create()
 81               .WithIdentity(triggerName, jobGroupName)
 82               .StartNow()
 83               .WithSimpleSchedule(x => x
 84                   .WithIntervalInSeconds(minutes)//seconds表示秒的时间间隔
 85                                                  //.WithIntervalInMinutes(minutes)//表示分钟的时间间隔
 86                   .RepeatForever())
 87               .Build();
 88             return _sched.ScheduleJob(jobDetail, trigger).Result;
 89         }
 90         public bool DeleteJobs(IReadOnlyCollection<JobKey> jobKeys)
 91         {
 92             return _sched.DeleteJobs(jobKeys).Result;
 93         }
 94         public IJobDetail GetJobDetail(JobKey jobKey)
 95         {
 96             return _sched.GetJobDetail(jobKey).Result;
 97         }
 98         public IReadOnlyCollection<string> GetJobGroupNames()
 99         {
100             return _sched.GetJobGroupNames().Result;
101         }
102         public IReadOnlyCollection<JobKey> GetJobKeys(GroupMatcher<JobKey> matcher)
103         {
104             return _sched.GetJobKeys(matcher).Result;
105         }
106         public bool Interrupt(JobKey jobKey)
107         {
108             return _sched.Interrupt(jobKey).Result;
109         }
110         public bool IsJobGroupPaused(string groupName)
111         {
112             return _sched.IsJobGroupPaused(groupName).Result;
113         }
114         public ITrigger GetTrigger(TriggerKey triggerKey)
115         {
116             return _sched.GetTrigger(triggerKey).Result;
117         }
118         public IReadOnlyCollection<string> GetTriggerGroupNames()
119         {
120             return _sched.GetTriggerGroupNames().Result;
121         }
122         public IReadOnlyCollection<TriggerKey> GetTriggerKeys(GroupMatcher<TriggerKey> matcher)
123         {
124             return _sched.GetTriggerKeys(matcher).Result;
125         }
126         public IReadOnlyCollection<ITrigger> GetTriggersOfJob(JobKey jobKey)
127         {
128             return _sched.GetTriggersOfJob(jobKey).Result;
129         }
130         public TriggerState GetTriggerState(TriggerKey triggerKey)
131         {
132             return _sched.GetTriggerState(triggerKey).Result;
133         }
134         public IReadOnlyCollection<string> GetPausedTriggerGroups()
135         {
136             return _sched.GetPausedTriggerGroups().Result;
137         }
138         public bool Interrupt(string fireInstanceId)
139         {
140             return _sched.Interrupt(fireInstanceId).Result;
141         }
142         public bool IsTriggerGroupPaused(string groupName)
143         {
144             return _sched.IsTriggerGroupPaused(groupName).Result;
145         }
146         public void PauseAll()
147         {
148             _sched.PauseAll();
149         }
150         public void PauseJobs(GroupMatcher<JobKey> matcher)
151         {
152             _sched.PauseJobs(matcher);
153         }
154         public void PauseTriggers(GroupMatcher<TriggerKey> matcher)
155         {
156             _sched.PauseTriggers(matcher);
157         }
158         public void ResumeAll()
159         {
160             _sched.ResumeAll();
161         }
162         public void ResumeJobs(GroupMatcher<JobKey> matcher)
163         {
164             _sched.ResumeJobs(matcher);
165         }
166         public void ResumeTriggers(GroupMatcher<TriggerKey> matcher)
167         {
168             _sched.ResumeTriggers(matcher);
169         }
170         public void ScheduleJobs(IReadOnlyDictionary<IJobDetail, IReadOnlyCollection<ITrigger>> triggersAndJobs, bool replace)
171         {
172             _sched.ScheduleJobs(triggersAndJobs, replace);
173         }
174         public DateTimeOffset? RescheduleJob(TriggerKey triggerKey, ITrigger newTrigger)
175         {
176             return _sched.RescheduleJob(triggerKey, newTrigger).Result;
177         }
178         public void Shutdown(bool waitForJobsToComplete)
179         {
180             _sched.Shutdown(waitForJobsToComplete);
181         }
182         public void Clear()
183         {
184             _sched.Clear();
185         }
186
187
188     }
189 }

   1 using StackExchange.Redis;
   2 using System;
   3 using System.Collections.Generic;
   4 using System.Configuration;
   5 using System.IO;
   6 using System.Linq;
   7 using System.Runtime.Serialization.Formatters.Binary;
   8 using System.Threading.Tasks;
   9 using Newtonsoft.Json;
  10
  11 namespace Common.Redis.StackExchange
  12 {
  13     /// <summary>
  14     /// V:1.2.6.0
  15     /// </summary>
  16     public class RedisHelper
  17     {
  18         #region private field
  19
  20         /// <summary>
  21         /// 连接字符串
  22         /// </summary>
  23         private static readonly string ConnectionString;
  24
  25         /// <summary>
  26         /// redis 连接对象
  27         /// </summary>
  28         private static IConnectionMultiplexer _connMultiplexer;
  29
  30         /// <summary>
  31         /// 默认的 Key 值(用来当作 RedisKey 的前缀)
  32         /// </summary>
  33         private static readonly string DefaultKey;
  34
  35         /// <summary>
  36         /// 锁
  37         /// </summary>
  38         private static readonly object Locker = new object();
  39
  40         /// <summary>
  41         /// 数据库
  42         /// </summary>
  43         private readonly IDatabase _db;
  44
  45         #endregion private field
  46
  47         #region 构造函数
  48
  49         static RedisHelper()
  50         {
  51             ConnectionString = ConfigurationManager.ConnectionStrings["RedisConnectionString"].ConnectionString;
  52             _connMultiplexer = ConnectionMultiplexer.Connect(ConnectionString);
  53             DefaultKey = ConfigurationManager.AppSettings["Redis.DefaultKey"];
  54             AddRegisterEvent();
  55         }
  56
  57         public RedisHelper(int db = 0)
  58         {
  59             _db = _connMultiplexer.GetDatabase(db);
  60         }
  61
  62         #endregion 构造函数
  63
  64         #region 其它
  65
  66         /// <summary>
  67         /// 获取 Redis 连接对象
  68         /// </summary>
  69         /// <returns></returns>
  70         public IConnectionMultiplexer GetConnectionRedisMultiplexer()
  71         {
  72             if (_connMultiplexer == null || !_connMultiplexer.IsConnected)
  73                 lock (Locker)
  74                 {
  75                     if (_connMultiplexer == null || !_connMultiplexer.IsConnected)
  76                         _connMultiplexer = ConnectionMultiplexer.Connect(ConnectionString);
  77                 }
  78
  79             return _connMultiplexer;
  80         }
  81
  82         public ITransaction GetTransaction()
  83         {
  84             return _db.CreateTransaction();
  85         }
  86
  87         #endregion 其它
  88
  89         #region 类型封装
  90
  91         #region String 操作
  92
  93         /// <summary>
  94         /// 设置 key 并保存字符串(如果 key 已存在,则覆盖值)
  95         /// </summary>
  96         /// <param name="redisKey"></param>
  97         /// <param name="redisValue"></param>
  98         /// <param name="expiry"></param>
  99         /// <returns></returns>
 100         public bool StringSet(string redisKey, string redisValue, TimeSpan? expiry = null)
 101         {
 102             redisKey = AddKeyPrefix(redisKey);
 103             return _db.StringSet(redisKey, redisValue, expiry);
 104         }
 105
 106         /// <summary>
 107         /// 保存多个 Key-value
 108         /// </summary>
 109         /// <param name="keyValuePairs"></param>
 110         /// <returns></returns>
 111         public bool StringSet(IEnumerable<KeyValuePair<string, string>> keyValuePairs)
 112         {
 113             var pairs = keyValuePairs.Select(x => new KeyValuePair<RedisKey, RedisValue>(AddKeyPrefix(x.Key), x.Value));
 114             return _db.StringSet(pairs.ToArray());
 115         }
 116
 117         /// <summary>
 118         /// 获取字符串
 119         /// </summary>
 120         /// <param name="redisKey"></param>
 121         /// <param name="expiry"></param>
 122         /// <returns></returns>
 123         public string StringGet(string redisKey, TimeSpan? expiry = null)
 124         {
 125             redisKey = AddKeyPrefix(redisKey);
 126             return _db.StringGet(redisKey);
 127         }
 128
 129         /// <summary>
 130         /// 存储一个对象(该对象会被序列化保存)
 131         /// </summary>
 132         /// <param name="redisKey"></param>
 133         /// <param name="redisValue"></param>
 134         /// <param name="expiry"></param>
 135         /// <returns></returns>
 136         public bool StringSet<T>(string redisKey, T redisValue, TimeSpan? expiry = null)
 137         {
 138             redisKey = AddKeyPrefix(redisKey);
 139             var json = Serialize(redisValue);
 140             return _db.StringSet(redisKey, json, expiry);
 141         }
 142
 143         /// <summary>
 144         /// 获取一个对象(会进行反序列化)
 145         /// </summary>
 146         /// <param name="redisKey"></param>
 147         /// <param name="expiry"></param>
 148         /// <returns></returns>
 149         public T StringGet<T>(string redisKey, TimeSpan? expiry = null)
 150         {
 151             redisKey = AddKeyPrefix(redisKey);
 152             return Deserialize<T>(_db.StringGet(redisKey));
 153         }
 154
 155         #region async
 156
 157         /// <summary>
 158         /// 保存一个字符串值
 159         /// </summary>
 160         /// <param name="redisKey"></param>
 161         /// <param name="redisValue"></param>
 162         /// <param name="expiry"></param>
 163         /// <returns></returns>
 164         public async Task<bool> StringSetAsync(string redisKey, string redisValue, TimeSpan? expiry = null)
 165         {
 166             redisKey = AddKeyPrefix(redisKey);
 167             return await _db.StringSetAsync(redisKey, redisValue, expiry);
 168         }
 169
 170         /// <summary>
 171         /// 保存一组字符串值
 172         /// </summary>
 173         /// <param name="keyValuePairs"></param>
 174         /// <returns></returns>
 175         public async Task<bool> StringSetAsync(IEnumerable<KeyValuePair<string, string>> keyValuePairs)
 176         {
 177             var pairs = keyValuePairs.Select(x => new KeyValuePair<RedisKey, RedisValue>(AddKeyPrefix(x.Key), x.Value));
 178             return await _db.StringSetAsync(pairs.ToArray());
 179         }
 180
 181         /// <summary>
 182         /// 获取单个值
 183         /// </summary>
 184         /// <param name="redisKey"></param>
 185         /// <param name="redisValue"></param>
 186         /// <param name="expiry"></param>
 187         /// <returns></returns>
 188         public async Task<string> StringGetAsync(string redisKey, string redisValue, TimeSpan? expiry = null)
 189         {
 190             redisKey = AddKeyPrefix(redisKey);
 191             return await _db.StringGetAsync(redisKey);
 192         }
 193
 194         /// <summary>
 195         /// 存储一个对象(该对象会被序列化保存)
 196         /// </summary>
 197         /// <param name="redisKey"></param>
 198         /// <param name="redisValue"></param>
 199         /// <param name="expiry"></param>
 200         /// <returns></returns>
 201         public async Task<bool> StringSetAsync<T>(string redisKey, T redisValue, TimeSpan? expiry = null)
 202         {
 203             redisKey = AddKeyPrefix(redisKey);
 204             var json = Serialize(redisValue);
 205             return await _db.StringSetAsync(redisKey, json, expiry);
 206         }
 207
 208         /// <summary>
 209         /// 获取一个对象(会进行反序列化)
 210         /// </summary>
 211         /// <param name="redisKey"></param>
 212         /// <param name="expiry"></param>
 213         /// <returns></returns>
 214         public async Task<T> StringGetAsync<T>(string redisKey, TimeSpan? expiry = null)
 215         {
 216             redisKey = AddKeyPrefix(redisKey);
 217             return Deserialize<T>(await _db.StringGetAsync(redisKey));
 218         }
 219
 220         #endregion async
 221
 222         #endregion String 操作
 223
 224         #region Hash 操作
 225
 226         /// <summary>
 227         /// 判断该字段是否存在 hash 中
 228         /// </summary>
 229         /// <param name="redisKey"></param>
 230         /// <param name="hashField"></param>
 231         /// <returns></returns>
 232         public bool HashExists(string redisKey, string hashField)
 233         {
 234             redisKey = AddKeyPrefix(redisKey);
 235             return _db.HashExists(redisKey, hashField);
 236         }
 237
 238         /// <summary>
 239         /// 从 hash 中移除指定字段
 240         /// </summary>
 241         /// <param name="redisKey"></param>
 242         /// <param name="hashField"></param>
 243         /// <returns></returns>
 244         public bool HashDelete(string redisKey, string hashField)
 245         {
 246             redisKey = AddKeyPrefix(redisKey);
 247             return _db.HashDelete(redisKey, hashField);
 248         }
 249
 250         /// <summary>
 251         /// 从 hash 中移除指定字段
 252         /// </summary>
 253         /// <param name="redisKey"></param>
 254         /// <param name="hashFields"></param>
 255         /// <returns></returns>
 256         public long HashDelete(string redisKey, IEnumerable<string> hashFields)
 257         {
 258             redisKey = AddKeyPrefix(redisKey);
 259             var fields = hashFields.Select(x => (RedisValue)x);
 260
 261             return _db.HashDelete(redisKey, fields.ToArray());
 262         }
 263
 264         /// <summary>
 265         /// 在 hash 设定值
 266         /// </summary>
 267         /// <param name="redisKey"></param>
 268         /// <param name="hashField"></param>
 269         /// <param name="value"></param>
 270         /// <returns></returns>
 271         public bool HashSet(string redisKey, string hashField, string value)
 272         {
 273             redisKey = AddKeyPrefix(redisKey);
 274             return _db.HashSet(redisKey, hashField, value);
 275         }
 276
 277         /// <summary>
 278         /// 在 hash 中设定值
 279         /// </summary>
 280         /// <param name="redisKey"></param>
 281         /// <param name="hashFields"></param>
 282         public void HashSet(string redisKey, IEnumerable<KeyValuePair<string, string>> hashFields)
 283         {
 284             redisKey = AddKeyPrefix(redisKey);
 285             var entries = hashFields.Select(x => new HashEntry(x.Key, x.Value));
 286
 287             _db.HashSet(redisKey, entries.ToArray());
 288         }
 289
 290         /// <summary>
 291         /// 在 hash 中获取值
 292         /// </summary>
 293         /// <param name="redisKey"></param>
 294         /// <param name="hashField"></param>
 295         /// <returns></returns>
 296         public string HashGet(string redisKey, string hashField)
 297         {
 298             redisKey = AddKeyPrefix(redisKey);
 299             return _db.HashGet(redisKey, hashField);
 300         }
 301
 302         /// <summary>
 303         /// 在 hash 中获取值
 304         /// </summary>
 305         /// <param name="redisKey"></param>
 306         /// <param name="hashFields"></param>
 307         /// <returns></returns>
 308         public IEnumerable<string> HashGet(string redisKey, IEnumerable<string> hashFields)
 309         {
 310             redisKey = AddKeyPrefix(redisKey);
 311             var fields = hashFields.Select(x => (RedisValue)x);
 312
 313             return ConvertStrings(_db.HashGet(redisKey, fields.ToArray()));
 314         }
 315
 316         /// <summary>
 317         /// 从 hash 返回所有的字段值
 318         /// </summary>
 319         /// <param name="redisKey"></param>
 320         /// <returns></returns>
 321         public IEnumerable<string> HashKeys(string redisKey)
 322         {
 323             redisKey = AddKeyPrefix(redisKey);
 324             return ConvertStrings(_db.HashKeys(redisKey));
 325         }
 326
 327         /// <summary>
 328         /// 返回 hash 中的所有值
 329         /// </summary>
 330         /// <param name="redisKey"></param>
 331         /// <returns></returns>
 332         public IEnumerable<string> HashValues(string redisKey)
 333         {
 334             redisKey = AddKeyPrefix(redisKey);
 335             return ConvertStrings(_db.HashValues(redisKey));
 336         }
 337
 338         /// <summary>
 339         /// 在 hash 设定值(序列化)
 340         /// </summary>
 341         /// <param name="redisKey"></param>
 342         /// <param name="hashField"></param>
 343         /// <param name="redisValue"></param>
 344         /// <returns></returns>
 345         public bool HashSet<T>(string redisKey, string hashField, T redisValue)
 346         {
 347             redisKey = AddKeyPrefix(redisKey);
 348             var json = Serialize(redisValue);
 349
 350             return _db.HashSet(redisKey, hashField, json);
 351         }
 352
 353         /// <summary>
 354         /// 在 hash 中获取值(反序列化)
 355         /// </summary>
 356         /// <param name="redisKey"></param>
 357         /// <param name="hashField"></param>
 358         /// <returns></returns>
 359         public T HashGet<T>(string redisKey, string hashField)
 360         {
 361             redisKey = AddKeyPrefix(redisKey);
 362
 363             return Deserialize<T>(_db.HashGet(redisKey, hashField));
 364         }
 365
 366         #region async
 367
 368         /// <summary>
 369         /// 判断该字段是否存在 hash 中
 370         /// </summary>
 371         /// <param name="redisKey"></param>
 372         /// <param name="hashField"></param>
 373         /// <returns></returns>
 374         public async Task<bool> HashExistsAsync(string redisKey, string hashField)
 375         {
 376             redisKey = AddKeyPrefix(redisKey);
 377             return await _db.HashExistsAsync(redisKey, hashField);
 378         }
 379
 380         /// <summary>
 381         /// 从 hash 中移除指定字段
 382         /// </summary>
 383         /// <param name="redisKey"></param>
 384         /// <param name="hashField"></param>
 385         /// <returns></returns>
 386         public async Task<bool> HashDeleteAsync(string redisKey, string hashField)
 387         {
 388             redisKey = AddKeyPrefix(redisKey);
 389             return await _db.HashDeleteAsync(redisKey, hashField);
 390         }
 391
 392         /// <summary>
 393         /// 从 hash 中移除指定字段
 394         /// </summary>
 395         /// <param name="redisKey"></param>
 396         /// <param name="hashFields"></param>
 397         /// <returns></returns>
 398         public async Task<long> HashDeleteAsync(string redisKey, IEnumerable<string> hashFields)
 399         {
 400             redisKey = AddKeyPrefix(redisKey);
 401             var fields = hashFields.Select(x => (RedisValue)x);
 402
 403             return await _db.HashDeleteAsync(redisKey, fields.ToArray());
 404         }
 405
 406         /// <summary>
 407         /// 在 hash 设定值
 408         /// </summary>
 409         /// <param name="redisKey"></param>
 410         /// <param name="hashField"></param>
 411         /// <param name="value"></param>
 412         /// <returns></returns>
 413         public async Task<bool> HashSetAsync(string redisKey, string hashField, string value)
 414         {
 415             redisKey = AddKeyPrefix(redisKey);
 416             return await _db.HashSetAsync(redisKey, hashField, value);
 417         }
 418
 419         /// <summary>
 420         /// 在 hash 中设定值
 421         /// </summary>
 422         /// <param name="redisKey"></param>
 423         /// <param name="hashFields"></param>
 424         public async Task HashSetAsync(string redisKey, IEnumerable<KeyValuePair<string, string>> hashFields)
 425         {
 426             redisKey = AddKeyPrefix(redisKey);
 427             var entries = hashFields.Select(x => new HashEntry(AddKeyPrefix(x.Key), x.Value));
 428             await _db.HashSetAsync(redisKey, entries.ToArray());
 429         }
 430
 431         /// <summary>
 432         /// 在 hash 中获取值
 433         /// </summary>
 434         /// <param name="redisKey"></param>
 435         /// <param name="hashField"></param>
 436         /// <returns></returns>
 437         public async Task<string> HashGetAsync(string redisKey, string hashField)
 438         {
 439             redisKey = AddKeyPrefix(redisKey);
 440             return await _db.HashGetAsync(redisKey, hashField);
 441         }
 442
 443         /// <summary>
 444         /// 在 hash 中获取值
 445         /// </summary>
 446         /// <param name="redisKey"></param>
 447         /// <param name="hashFields"></param>
 448         /// <param name="value"></param>
 449         /// <returns></returns>
 450         public async Task<IEnumerable<string>> HashGetAsync(string redisKey, IEnumerable<string> hashFields,
 451             string value)
 452         {
 453             redisKey = AddKeyPrefix(redisKey);
 454             var fields = hashFields.Select(x => (RedisValue)x);
 455
 456             return ConvertStrings(await _db.HashGetAsync(redisKey, fields.ToArray()));
 457         }
 458
 459         /// <summary>
 460         /// 从 hash 返回所有的字段值
 461         /// </summary>
 462         /// <param name="redisKey"></param>
 463         /// <returns></returns>
 464         public async Task<IEnumerable<string>> HashKeysAsync(string redisKey)
 465         {
 466             redisKey = AddKeyPrefix(redisKey);
 467             return ConvertStrings(await _db.HashKeysAsync(redisKey));
 468         }
 469
 470         /// <summary>
 471         /// 返回 hash 中的所有值
 472         /// </summary>
 473         /// <param name="redisKey"></param>
 474         /// <returns></returns>
 475         public async Task<IEnumerable<string>> HashValuesAsync(string redisKey)
 476         {
 477             redisKey = AddKeyPrefix(redisKey);
 478             return ConvertStrings(await _db.HashValuesAsync(redisKey));
 479         }
 480
 481         /// <summary>
 482         /// 在 hash 设定值(序列化)
 483         /// </summary>
 484         /// <param name="redisKey"></param>
 485         /// <param name="hashField"></param>
 486         /// <param name="value"></param>
 487         /// <returns></returns>
 488         public async Task<bool> HashSetAsync<T>(string redisKey, string hashField, T value)
 489         {
 490             redisKey = AddKeyPrefix(redisKey);
 491             var json = Serialize(value);
 492             return await _db.HashSetAsync(redisKey, hashField, json);
 493         }
 494
 495         /// <summary>
 496         /// 在 hash 中获取值(反序列化)
 497         /// </summary>
 498         /// <param name="redisKey"></param>
 499         /// <param name="hashField"></param>
 500         /// <returns></returns>
 501         public async Task<T> HashGetAsync<T>(string redisKey, string hashField)
 502         {
 503             redisKey = AddKeyPrefix(redisKey);
 504             return Deserialize<T>(await _db.HashGetAsync(redisKey, hashField));
 505         }
 506
 507         #endregion async
 508
 509         #endregion Hash 操作
 510
 511         #region List 操作
 512
 513         /// <summary>
 514         /// 移除并返回存储在该键列表的第一个元素
 515         /// </summary>
 516         /// <param name="redisKey"></param>
 517         /// <returns></returns>
 518         public string ListLeftPop(string redisKey)
 519         {
 520             redisKey = AddKeyPrefix(redisKey);
 521             return _db.ListLeftPop(redisKey);
 522         }
 523
 524         /// <summary>
 525         /// 移除并返回存储在该键列表的最后一个元素
 526         /// </summary>
 527         /// <param name="redisKey"></param>
 528         /// <returns></returns>
 529         public string ListRightPop(string redisKey)
 530         {
 531             redisKey = AddKeyPrefix(redisKey);
 532             return _db.ListRightPop(redisKey);
 533         }
 534
 535         /// <summary>
 536         /// 移除列表指定键上与该值相同的元素
 537         /// </summary>
 538         /// <param name="redisKey"></param>
 539         /// <param name="redisValue"></param>
 540         /// <returns></returns>
 541         public long ListRemove(string redisKey, string redisValue)
 542         {
 543             redisKey = AddKeyPrefix(redisKey);
 544             return _db.ListRemove(redisKey, redisValue);
 545         }
 546
 547         /// <summary>
 548         /// 在列表尾部插入值。如果键不存在,先创建再插入值
 549         /// </summary>
 550         /// <param name="redisKey"></param>
 551         /// <param name="redisValue"></param>
 552         /// <returns></returns>
 553         public long ListRightPush(string redisKey, string redisValue)
 554         {
 555             redisKey = AddKeyPrefix(redisKey);
 556             return _db.ListRightPush(redisKey, redisValue);
 557         }
 558
 559         /// <summary>
 560         /// 在列表头部插入值。如果键不存在,先创建再插入值
 561         /// </summary>
 562         /// <param name="redisKey"></param>
 563         /// <param name="redisValue"></param>
 564         /// <returns></returns>
 565         public long ListLeftPush(string redisKey, string redisValue)
 566         {
 567             redisKey = AddKeyPrefix(redisKey);
 568             return _db.ListLeftPush(redisKey, redisValue);
 569         }
 570
 571         /// <summary>
 572         /// 返回列表上该键的长度,如果不存在,返回 0
 573         /// </summary>
 574         /// <param name="redisKey"></param>
 575         /// <returns></returns>
 576         public long ListLength(string redisKey)
 577         {
 578             redisKey = AddKeyPrefix(redisKey);
 579             return _db.ListLength(redisKey);
 580         }
 581
 582         /// <summary>
 583         /// 返回在该列表上键所对应的元素
 584         /// </summary>
 585         /// <param name="redisKey"></param>
 586         /// <param name="start"></param>
 587         /// <param name="stop"></param>
 588         /// <returns></returns>
 589         public IEnumerable<string> ListRange(string redisKey, long start = 0L, long stop = -1L)
 590         {
 591             redisKey = AddKeyPrefix(redisKey);
 592             return ConvertStrings(_db.ListRange(redisKey, start, stop));
 593         }
 594
 595         /// <summary>
 596         /// 移除并返回存储在该键列表的第一个元素
 597         /// </summary>
 598         /// <param name="redisKey"></param>
 599         /// <returns></returns>
 600         public T ListLeftPop<T>(string redisKey)
 601         {
 602             redisKey = AddKeyPrefix(redisKey);
 603             return Deserialize<T>(_db.ListLeftPop(redisKey));
 604         }
 605
 606         /// <summary>
 607         /// 移除并返回存储在该键列表的最后一个元素
 608         /// </summary>
 609         /// <param name="redisKey"></param>
 610         /// <returns></returns>
 611         public T ListRightPop<T>(string redisKey)
 612         {
 613             redisKey = AddKeyPrefix(redisKey);
 614             return Deserialize<T>(_db.ListRightPop(redisKey));
 615         }
 616
 617         /// <summary>
 618         /// 在列表尾部插入值。如果键不存在,先创建再插入值
 619         /// </summary>
 620         /// <param name="redisKey"></param>
 621         /// <param name="redisValue"></param>
 622         /// <returns></returns>
 623         public long ListRightPush<T>(string redisKey, T redisValue)
 624         {
 625             redisKey = AddKeyPrefix(redisKey);
 626             return _db.ListRightPush(redisKey, Serialize(redisValue));
 627         }
 628
 629         /// <summary>
 630         /// 在列表头部插入值。如果键不存在,先创建再插入值
 631         /// </summary>
 632         /// <param name="redisKey"></param>
 633         /// <param name="redisValue"></param>
 634         /// <returns></returns>
 635         public long ListLeftPush<T>(string redisKey, T redisValue)
 636         {
 637             redisKey = AddKeyPrefix(redisKey);
 638             return _db.ListLeftPush(redisKey, Serialize(redisValue));
 639         }
 640
 641         #region List-async
 642
 643         /// <summary>
 644         /// 移除并返回存储在该键列表的第一个元素
 645         /// </summary>
 646         /// <param name="redisKey"></param>
 647         /// <returns></returns>
 648         public async Task<string> ListLeftPopAsync(string redisKey)
 649         {
 650             redisKey = AddKeyPrefix(redisKey);
 651             return await _db.ListLeftPopAsync(redisKey);
 652         }
 653
 654         /// <summary>
 655         /// 移除并返回存储在该键列表的最后一个元素
 656         /// </summary>
 657         /// <param name="redisKey"></param>
 658         /// <returns></returns>
 659         public async Task<string> ListRightPopAsync(string redisKey)
 660         {
 661             redisKey = AddKeyPrefix(redisKey);
 662             return await _db.ListRightPopAsync(redisKey);
 663         }
 664
 665         /// <summary>
 666         /// 移除列表指定键上与该值相同的元素
 667         /// </summary>
 668         /// <param name="redisKey"></param>
 669         /// <param name="redisValue"></param>
 670         /// <returns></returns>
 671         public async Task<long> ListRemoveAsync(string redisKey, string redisValue)
 672         {
 673             redisKey = AddKeyPrefix(redisKey);
 674             return await _db.ListRemoveAsync(redisKey, redisValue);
 675         }
 676
 677         /// <summary>
 678         /// 在列表尾部插入值。如果键不存在,先创建再插入值
 679         /// </summary>
 680         /// <param name="redisKey"></param>
 681         /// <param name="redisValue"></param>
 682         /// <returns></returns>
 683         public async Task<long> ListRightPushAsync(string redisKey, string redisValue)
 684         {
 685             redisKey = AddKeyPrefix(redisKey);
 686             return await _db.ListRightPushAsync(redisKey, redisValue);
 687         }
 688
 689         /// <summary>
 690         /// 在列表头部插入值。如果键不存在,先创建再插入值
 691         /// </summary>
 692         /// <param name="redisKey"></param>
 693         /// <param name="redisValue"></param>
 694         /// <returns></returns>
 695         public async Task<long> ListLeftPushAsync(string redisKey, string redisValue)
 696         {
 697             redisKey = AddKeyPrefix(redisKey);
 698             return await _db.ListLeftPushAsync(redisKey, redisValue);
 699         }
 700
 701         /// <summary>
 702         /// 返回列表上该键的长度,如果不存在,返回 0
 703         /// </summary>
 704         /// <param name="redisKey"></param>
 705         /// <returns></returns>
 706         public async Task<long> ListLengthAsync(string redisKey)
 707         {
 708             redisKey = AddKeyPrefix(redisKey);
 709             return await _db.ListLengthAsync(redisKey);
 710         }
 711
 712         /// <summary>
 713         /// 返回在该列表上键所对应的元素
 714         /// </summary>
 715         /// <param name="redisKey"></param>
 716         /// <param name="start"></param>
 717         /// <param name="stop"></param>
 718         /// <returns></returns>
 719         public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, long start = 0L, long stop = -1L)
 720         {
 721             redisKey = AddKeyPrefix(redisKey);
 722             var query = await _db.ListRangeAsync(redisKey, start, stop);
 723             return query.Select(x => x.ToString());
 724         }
 725
 726         /// <summary>
 727         /// 移除并返回存储在该键列表的第一个元素
 728         /// </summary>
 729         /// <param name="redisKey"></param>
 730         /// <returns></returns>
 731         public async Task<T> ListLeftPopAsync<T>(string redisKey)
 732         {
 733             redisKey = AddKeyPrefix(redisKey);
 734             return Deserialize<T>(await _db.ListLeftPopAsync(redisKey));
 735         }
 736
 737         /// <summary>
 738         /// 移除并返回存储在该键列表的最后一个元素
 739         /// </summary>
 740         /// <param name="redisKey"></param>
 741         /// <returns></returns>
 742         public async Task<T> ListRightPopAsync<T>(string redisKey)
 743         {
 744             redisKey = AddKeyPrefix(redisKey);
 745             return Deserialize<T>(await _db.ListRightPopAsync(redisKey));
 746         }
 747
 748         /// <summary>
 749         /// 在列表尾部插入值。如果键不存在,先创建再插入值
 750         /// </summary>
 751         /// <param name="redisKey"></param>
 752         /// <param name="redisValue"></param>
 753         /// <returns></returns>
 754         public async Task<long> ListRightPushAsync<T>(string redisKey, T redisValue)
 755         {
 756             redisKey = AddKeyPrefix(redisKey);
 757             return await _db.ListRightPushAsync(redisKey, Serialize(redisValue));
 758         }
 759
 760         /// <summary>
 761         /// 在列表头部插入值。如果键不存在,先创建再插入值
 762         /// </summary>
 763         /// <param name="redisKey"></param>
 764         /// <param name="redisValue"></param>
 765         /// <returns></returns>
 766         public async Task<long> ListLeftPushAsync<T>(string redisKey, T redisValue)
 767         {
 768             redisKey = AddKeyPrefix(redisKey);
 769             return await _db.ListLeftPushAsync(redisKey, Serialize(redisValue));
 770         }
 771
 772         #endregion List-async
 773
 774         #endregion List 操作
 775
 776         #region SortedSet 操作
 777
 778         /// <summary>
 779         /// SortedSet 新增
 780         /// </summary>
 781         /// <param name="redisKey"></param>
 782         /// <param name="member"></param>
 783         /// <param name="score"></param>
 784         /// <returns></returns>
 785         public bool SortedSetAdd(string redisKey, string member, double score)
 786         {
 787             redisKey = AddKeyPrefix(redisKey);
 788             return _db.SortedSetAdd(redisKey, member, score);
 789         }
 790
 791         /// <summary>
 792         /// 在有序集合中返回指定范围的元素,默认情况下从低到高。
 793         /// </summary>
 794         /// <param name="redisKey"></param>
 795         /// <param name="start"></param>
 796         /// <param name="stop"></param>
 797         /// <param name="order"></param>
 798         /// <returns></returns>
 799         public IEnumerable<string> SortedSetRangeByRank(string redisKey, long start = 0L, long stop = -1L,
 800             Order order = Order.Ascending)
 801         {
 802             redisKey = AddKeyPrefix(redisKey);
 803             return _db.SortedSetRangeByRank(redisKey, start, stop, (Order)order).Select(x => x.ToString());
 804         }
 805
 806         /// <summary>
 807         /// 返回有序集合的元素个数
 808         /// </summary>
 809         /// <param name="redisKey"></param>
 810         /// <returns></returns>
 811         public long SortedSetLength(string redisKey)
 812         {
 813             redisKey = AddKeyPrefix(redisKey);
 814             return _db.SortedSetLength(redisKey);
 815         }
 816
 817         /// <summary>
 818         /// 返回有序集合的元素个数
 819         /// </summary>
 820         /// <param name="redisKey"></param>
 821         /// <param name="memebr"></param>
 822         /// <returns></returns>
 823         public bool SortedSetLength(string redisKey, string memebr)
 824         {
 825             redisKey = AddKeyPrefix(redisKey);
 826             return _db.SortedSetRemove(redisKey, memebr);
 827         }
 828
 829         /// <summary>
 830         /// SortedSet 新增
 831         /// </summary>
 832         /// <param name="redisKey"></param>
 833         /// <param name="member"></param>
 834         /// <param name="score"></param>
 835         /// <returns></returns>
 836         public bool SortedSetAdd<T>(string redisKey, T member, double score)
 837         {
 838             redisKey = AddKeyPrefix(redisKey);
 839             var json = Serialize(member);
 840
 841             return _db.SortedSetAdd(redisKey, json, score);
 842         }
 843
 844         /// <summary>
 845         /// 增量的得分排序的集合中的成员存储键值键按增量
 846         /// </summary>
 847         /// <param name="redisKey"></param>
 848         /// <param name="member"></param>
 849         /// <param name="value"></param>
 850         /// <returns></returns>
 851         public double SortedSetIncrement(string redisKey, string member, double value = 1)
 852         {
 853             redisKey = AddKeyPrefix(redisKey);
 854             return _db.SortedSetIncrement(redisKey, member, value);
 855         }
 856
 857         #region SortedSet-Async
 858
 859         /// <summary>
 860         /// SortedSet 新增
 861         /// </summary>
 862         /// <param name="redisKey"></param>
 863         /// <param name="member"></param>
 864         /// <param name="score"></param>
 865         /// <returns></returns>
 866         public async Task<bool> SortedSetAddAsync(string redisKey, string member, double score)
 867         {
 868             redisKey = AddKeyPrefix(redisKey);
 869             return await _db.SortedSetAddAsync(redisKey, member, score);
 870         }
 871
 872         /// <summary>
 873         /// 在有序集合中返回指定范围的元素,默认情况下从低到高。
 874         /// </summary>
 875         /// <param name="redisKey"></param>
 876         /// <returns></returns>
 877         public async Task<IEnumerable<string>> SortedSetRangeByRankAsync(string redisKey)
 878         {
 879             redisKey = AddKeyPrefix(redisKey);
 880             return ConvertStrings(await _db.SortedSetRangeByRankAsync(redisKey));
 881         }
 882
 883         /// <summary>
 884         /// 返回有序集合的元素个数
 885         /// </summary>
 886         /// <param name="redisKey"></param>
 887         /// <returns></returns>
 888         public async Task<long> SortedSetLengthAsync(string redisKey)
 889         {
 890             redisKey = AddKeyPrefix(redisKey);
 891             return await _db.SortedSetLengthAsync(redisKey);
 892         }
 893
 894         /// <summary>
 895         /// 返回有序集合的元素个数
 896         /// </summary>
 897         /// <param name="redisKey"></param>
 898         /// <param name="memebr"></param>
 899         /// <returns></returns>
 900         public async Task<bool> SortedSetRemoveAsync(string redisKey, string memebr)
 901         {
 902             redisKey = AddKeyPrefix(redisKey);
 903             return await _db.SortedSetRemoveAsync(redisKey, memebr);
 904         }
 905
 906         /// <summary>
 907         /// SortedSet 新增
 908         /// </summary>
 909         /// <param name="redisKey"></param>
 910         /// <param name="member"></param>
 911         /// <param name="score"></param>
 912         /// <returns></returns>
 913         public async Task<bool> SortedSetAddAsync<T>(string redisKey, T member, double score)
 914         {
 915             redisKey = AddKeyPrefix(redisKey);
 916             var json = Serialize(member);
 917
 918             return await _db.SortedSetAddAsync(redisKey, json, score);
 919         }
 920
 921         /// <summary>
 922         /// 增量的得分排序的集合中的成员存储键值键按增量
 923         /// </summary>
 924         /// <param name="redisKey"></param>
 925         /// <param name="member"></param>
 926         /// <param name="value"></param>
 927         /// <returns></returns>
 928         public Task<double> SortedSetIncrementAsync(string redisKey, string member, double value = 1)
 929         {
 930             redisKey = AddKeyPrefix(redisKey);
 931             return _db.SortedSetIncrementAsync(redisKey, member, value);
 932         }
 933
 934         #endregion SortedSet-Async
 935
 936         #endregion SortedSet 操作
 937
 938         #endregion 类型封装
 939
 940         #region 将object序列化读写
 941         public void ListSet<T>(string key, List<T> value)
 942         {
 943             foreach (var single in value)
 944             {
 945                 var jsonobj = JsonConvert.SerializeObject(single); //序列化
 946                 this.ListLeftPush(key, jsonobj); //要一个个的插入
 947
 948             }
 949         }
 950         public List<T> ListGet<T>(string key)
 951         {
 952             var jsonArr = this.ListRange(key);
 953             List<T> result = new List<T>();
 954             foreach (var item in jsonArr)
 955             {
 956                 var model = JsonConvert.DeserializeObject<T>(item); //反序列化
 957                 result.Add(model);
 958             }
 959             return result;
 960         }
 961         #endregion
 962
 963         #region KEY 操作
 964
 965         /// <summary>
 966         /// 删除单个key
 967         /// </summary>
 968         /// <param name="redisKey"></param>
 969         /// <returns>是否删除成功</returns>
 970         public bool KeyDelete(string redisKey)
 971         {
 972             redisKey = AddKeyPrefix(redisKey);
 973             return _db.KeyDelete(redisKey);
 974         }
 975
 976         /// <summary>
 977         /// 删除多个key
 978         /// </summary>
 979         /// <param name="redisKeys"></param>
 980         /// <returns>成功删除的个数</returns>
 981         public long KeyDelete(IEnumerable<string> redisKeys)
 982         {
 983             var keys = redisKeys.Select(x => (RedisKey)AddKeyPrefix(x));
 984             return _db.KeyDelete(keys.ToArray());
 985         }
 986
 987         /// <summary>
 988         /// 校验 Key 是否存在
 989         /// </summary>
 990         /// <param name="redisKey"></param>
 991         /// <returns></returns>
 992         public bool KeyExists(string redisKey)
 993         {
 994             redisKey = AddKeyPrefix(redisKey);
 995             return _db.KeyExists(redisKey);
 996         }
 997
 998         /// <summary>
 999         /// 重命名 Key
1000         /// </summary>
1001         /// <param name="redisKey"></param>
1002         /// <param name="redisNewKey"></param>
1003         /// <returns></returns>
1004         public bool KeyRename(string redisKey, string redisNewKey)
1005         {
1006             redisKey = AddKeyPrefix(redisKey);
1007             return _db.KeyRename(redisKey, redisNewKey);
1008         }
1009
1010         /// <summary>
1011         /// 设置 Key 的时间
1012         /// </summary>
1013         /// <param name="redisKey"></param>
1014         /// <param name="expiry"></param>
1015         /// <returns></returns>
1016         public bool KeyExpire(string redisKey, TimeSpan? expiry)
1017         {
1018             redisKey = AddKeyPrefix(redisKey);
1019             return _db.KeyExpire(redisKey, expiry);
1020         }
1021
1022         #region key-async
1023
1024         /// <summary>
1025         /// 移除指定 Key
1026         /// </summary>
1027         /// <param name="redisKey"></param>
1028         /// <returns></returns>
1029         public async Task<bool> KeyDeleteAsync(string redisKey)
1030         {
1031             redisKey = AddKeyPrefix(redisKey);
1032             return await _db.KeyDeleteAsync(redisKey);
1033         }
1034
1035         /// <summary>
1036         /// 移除指定 Key
1037         /// </summary>
1038         /// <param name="redisKeys"></param>
1039         /// <returns></returns>
1040         public async Task<long> KeyDeleteAsync(IEnumerable<string> redisKeys)
1041         {
1042             var keys = redisKeys.Select(x => (RedisKey)AddKeyPrefix(x));
1043             return await _db.KeyDeleteAsync(keys.ToArray());
1044         }
1045
1046         /// <summary>
1047         /// 校验 Key 是否存在
1048         /// </summary>
1049         /// <param name="redisKey"></param>
1050         /// <returns></returns>
1051         public async Task<bool> KeyExistsAsync(string redisKey)
1052         {
1053             redisKey = AddKeyPrefix(redisKey);
1054             return await _db.KeyExistsAsync(redisKey);
1055         }
1056
1057         /// <summary>
1058         /// 重命名 Key
1059         /// </summary>
1060         /// <param name="redisKey"></param>
1061         /// <param name="redisNewKey"></param>
1062         /// <returns></returns>
1063         public async Task<bool> KeyRenameAsync(string redisKey, string redisNewKey)
1064         {
1065             redisKey = AddKeyPrefix(redisKey);
1066             return await _db.KeyRenameAsync(redisKey, redisNewKey);
1067         }
1068
1069         /// <summary>
1070         /// 设置 Key 的时间
1071         /// </summary>
1072         /// <param name="redisKey"></param>
1073         /// <param name="expiry"></param>
1074         /// <returns></returns>
1075         public async Task<bool> KeyExpireAsync(string redisKey, TimeSpan? expiry)
1076         {
1077             redisKey = AddKeyPrefix(redisKey);
1078             return await _db.KeyExpireAsync(redisKey, expiry);
1079         }
1080
1081         #endregion key-async
1082
1083         #endregion KEY 操作
1084
1085         #region private method
1086
1087         /// <summary>
1088         /// 添加 Key 的前缀
1089         /// </summary>
1090         /// <param name="key"></param>
1091         /// <returns></returns>
1092         private static string AddKeyPrefix(string key)
1093         {
1094             return DefaultKey + ":" + key;
1095         }
1096
1097         /// <summary>
1098         /// 转换为字符串
1099         /// </summary>
1100         /// <typeparam name="T"></typeparam>
1101         /// <param name="list"></param>
1102         /// <returns></returns>
1103         private static IEnumerable<string> ConvertStrings<T>(IEnumerable<T> list) where T : struct
1104         {
1105             if (list == null) throw new ArgumentNullException(nameof(list));
1106             return list.Select(x => x.ToString());
1107         }
1108
1109         #region 注册事件
1110
1111         /// <summary>
1112         /// 添加注册事件
1113         /// </summary>
1114         private static void AddRegisterEvent()
1115         {
1116             _connMultiplexer.ConnectionRestored += ConnMultiplexer_ConnectionRestored;
1117             _connMultiplexer.ConnectionFailed += ConnMultiplexer_ConnectionFailed;
1118             _connMultiplexer.ErrorMessage += ConnMultiplexer_ErrorMessage;
1119             _connMultiplexer.ConfigurationChanged += ConnMultiplexer_ConfigurationChanged;
1120             _connMultiplexer.HashSlotMoved += ConnMultiplexer_HashSlotMoved;
1121             _connMultiplexer.InternalError += ConnMultiplexer_InternalError;
1122             _connMultiplexer.ConfigurationChangedBroadcast += ConnMultiplexer_ConfigurationChangedBroadcast;
1123         }
1124
1125         /// <summary>
1126         /// 重新配置广播时(通常意味着主从同步更改)
1127         /// </summary>
1128         /// <param name="sender"></param>
1129         /// <param name="e"></param>
1130         private static void ConnMultiplexer_ConfigurationChangedBroadcast(object sender, EndPointEventArgs e)
1131         {
1132             Console.WriteLine($"{nameof(ConnMultiplexer_ConfigurationChangedBroadcast)}: {e.EndPoint}");
1133         }
1134
1135         /// <summary>
1136         /// 发生内部错误时(主要用于调试)
1137         /// </summary>
1138         /// <param name="sender"></param>
1139         /// <param name="e"></param>
1140         private static void ConnMultiplexer_InternalError(object sender, InternalErrorEventArgs e)
1141         {
1142             Console.WriteLine($"{nameof(ConnMultiplexer_InternalError)}: {e.Exception}");
1143         }
1144
1145         /// <summary>
1146         /// 更改集群时
1147         /// </summary>
1148         /// <param name="sender"></param>
1149         /// <param name="e"></param>
1150         private static void ConnMultiplexer_HashSlotMoved(object sender, HashSlotMovedEventArgs e)
1151         {
1152             Console.WriteLine(
1153                 $"{nameof(ConnMultiplexer_HashSlotMoved)}: {nameof(e.OldEndPoint)}-{e.OldEndPoint} To {nameof(e.NewEndPoint)}-{e.NewEndPoint}, ");
1154         }
1155
1156         /// <summary>
1157         /// 配置更改时
1158         /// </summary>
1159         /// <param name="sender"></param>
1160         /// <param name="e"></param>
1161         private static void ConnMultiplexer_ConfigurationChanged(object sender, EndPointEventArgs e)
1162         {
1163             Console.WriteLine($"{nameof(ConnMultiplexer_ConfigurationChanged)}: {e.EndPoint}");
1164         }
1165
1166         /// <summary>
1167         /// 发生错误时
1168         /// </summary>
1169         /// <param name="sender"></param>
1170         /// <param name="e"></param>
1171         private static void ConnMultiplexer_ErrorMessage(object sender, RedisErrorEventArgs e)
1172         {
1173             Console.WriteLine($"{nameof(ConnMultiplexer_ErrorMessage)}: {e.Message}");
1174         }
1175
1176         /// <summary>
1177         /// 物理连接失败时
1178         /// </summary>
1179         /// <param name="sender"></param>
1180         /// <param name="e"></param>
1181         private static void ConnMultiplexer_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
1182         {
1183             Console.WriteLine($"{nameof(ConnMultiplexer_ConnectionFailed)}: {e.Exception}");
1184         }
1185
1186         /// <summary>
1187         /// 建立物理连接时
1188         /// </summary>
1189         /// <param name="sender"></param>
1190         /// <param name="e"></param>
1191         private static void ConnMultiplexer_ConnectionRestored(object sender, ConnectionFailedEventArgs e)
1192         {
1193             Console.WriteLine($"{nameof(ConnMultiplexer_ConnectionRestored)}: {e.Exception}");
1194         }
1195
1196         #endregion 注册事件
1197
1198         /// <summary>
1199         /// 序列化
1200         /// </summary>
1201         /// <param name="obj"></param>
1202         /// <returns></returns>
1203         private static byte[] Serialize(object obj)
1204         {
1205             if (obj == null)
1206                 return null;
1207
1208             var binaryFormatter = new BinaryFormatter();
1209             using (var memoryStream = new MemoryStream())
1210             {
1211                 binaryFormatter.Serialize(memoryStream, obj);
1212                 var data = memoryStream.ToArray();
1213                 return data;
1214             }
1215         }
1216
1217         /// <summary>
1218         /// 反序列化
1219         /// </summary>
1220         /// <typeparam name="T"></typeparam>
1221         /// <param name="data"></param>
1222         /// <returns></returns>
1223         private static T Deserialize<T>(byte[] data)
1224         {
1225             if (data == null)
1226                 return default(T);
1227
1228             var binaryFormatter = new BinaryFormatter();
1229             using (var memoryStream = new MemoryStream(data))
1230             {
1231                 var result = (T)binaryFormatter.Deserialize(memoryStream);
1232                 return result;
1233             }
1234         }
1235
1236         #endregion private method
1237
1238     }
1239 }

以上代码未全部测试,如若使用请自行验证。如代码有误请帮忙指出,谢谢!

原文地址:https://www.cnblogs.com/hua66/p/9600085.html

时间: 2024-10-05 08:14:58

Redis分布式队列和缓存更新的相关文章

Redis分布式队列解决文件并发的问题

1.首先将捕获的异常写到Redis的队列中 1 public class MyExceptionAttribute : HandleErrorAttribute 2 { 3 public static IRedisClientsManager clientManager = new PooledRedisClientManager(new string[] { "127.0.0.1:6379", "192.168.1.2:6379" }); 4 public sta

使用Redis分布式队列

1.这是处理异常的类 public class MyExceptionAttribute:HandleErrorAttribute { //public static Queue<Exception> ExceptionQueue = new Queue<Exception>(); //项目中使用下面方式创建redisclient public static IRedisClientsManager clientManager = new PooledRedisClientMana

Redis系列十:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级

一.缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机.从而形成一系列连锁反应,造成整个系统崩溃. 缓存正常从Redis中获取,示意图如下: 缓存失效瞬间示意图如下: 缓存雪崩的解决方案: (1)碰到这种情况,一般并发量不是特别多的时候,使用最多的解决方案是加锁排队,伪代码如下: 加锁排队只是为了

redis缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级

一.缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机.从而形成一系列连锁反应,造成整个系统崩溃. 缓存正常从Redis中获取,示意图如下: 缓存失效瞬间示意图如下: 缓存雪崩的解决方案: (1)碰到这种情况,一般并发量不是特别多的时候,使用最多的解决方案是加锁排队,伪代码如下: 加锁排队只是为了

redis分布式锁和消息队列

最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP等,不能在内存中使用锁,或者如Java这样的,需要一下更为简单的锁校验的时候,redis分布式锁的使用就足够满足了.redis的分布式锁其实就是基于setnx方法和redis对key可设置有效时间的功能来实现的.基本用法比较简单. public boolean tryLock(String lock

转载自haier_jiang的专栏基于redis分布式缓存实现

简单说明下,写此文章算是对自己近一段工作的总结,希望能对你有点帮助,同时也是自己的一点小积累. 一.为什么选择redis 在项目中使用redis做为缓存,还没有使用memcache,考虑因素主要有两点: 1.redis丰富的数据结构,其hash,list,set以及功能丰富的String的支持,对于实际项目中的使用有很大的帮忙.(可参考官网redis.io) 2.redis单点的性能也非常高效(利用项目中的数据测试优于memcache). 基于以上考虑,因此选用了redis来做为缓存应用. 二.

JavaWeb项目架构之Redis分布式日志队列

架构.分布式.日志队列,标题自己都看着唬人,其实就是一个日志收集的功能,只不过中间加了一个Redis做消息队列罢了. 前言 为什么需要消息队列? 当系统中出现"生产"和"消费"的速度或稳定性等因素不一致的时候,就需要消息队列,作为抽象层,弥合双方的差异. 比如我们系统中常见的邮件.短信发送,把这些不需要及时响应的功能写入队列,异步处理请求,减少响应时间. 如何实现? 成熟的JMS消息队列中间件产品市面上有很多,但是基于目前项目的架构以及部署情况,我们采用Redis做

SpringBoot集成Redis分布式锁以及Redis缓存

https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 <!-- 引入redis依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifa

搞懂分布式技术15:缓存更新的套路

搞懂分布式技术15:缓存更新的套路 缓存更新的套路 看到好些人在写更新缓存数据代码时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载的缓存中.然而,这个是逻辑是错误的.试想,两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后在放到缓存中之前更新操作获得了执行权更新了数据库,于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了. 我不知道为什么这么多人用的都是这个逻辑,当我在微博上发了这个贴以后,我发现