StackExchange.Redis通用封装类分享
前两天朋友问我,有没有使用过StackExchange.Redis,问我要个封装类,由于之前都是使用ServiceStack.Redis,由于ServiceStack.Redis v4版本后是收费版的,所以现在也很有公司都在使用StackExchange.Redis而抛弃ServiceStack.Redis了。其实个人觉得,两个驱动都不错,只是由于ServiceStack.Redis收费导致目前很多公司都是基于V3版本的使用,也有人说V3版本有很多Bug,没有维护和升级,不过至少目前我是没发现Bug。
不过ServiceStack.Redis同StackExchange.Redis比较,抛开收费的来说,确认比StackExchange.Redis 更有优势。StackExchange.Redis文档很少,更不要说国内的文档了,连github上面对应的介绍文档都是很片面,这点我真的觉得StackExchange.Redis的作者至少要完善下文档,很多都是要看源码的例子才有。网上对StackExchange.Redis的使用例子也比ServiceStack.Redis少得多,不是说没人用,只是我查来查去,大部分都是基于String类型的数据进行使用的封装类,对于List,SortedSet,Hash的封装操作都很少,基本都是东写一点,西写一点,很难找到完整的。在参考了一些文章和源码后,这里提供一个自己封装的类,基本提供对于各种类型的使用封装,提供给大家学习使用,如果有哪里写的不好的,大家也可以互相交流。
ConnectionMultiplexer 封装
首先是 ConnectionMultiplexer 的封装,ConnectionMultiplexer对象是StackExchange.Redis最中枢的对象。这个类的实例需要被整个应用程序域共享和重用的,所以不需要在每个操作中不停的创建该对象的实例,一般都是使用单例来创建和存放这个对象,这个在官网上也有说明。
1 /// <summary>
2 /// ConnectionMultiplexer对象管理帮助类
3 /// </summary>
4 public static class RedisConnectionHelp
5 {
6 //系统自定义Key前缀
7 public static readonly string SysCustomKey = ConfigurationManager.AppSettings["redisKey"] ?? "";
8
9 //"127.0.0.1:6379,allowadmin=true
10 private static readonly string RedisConnectionString = ConfigurationManager.ConnectionStrings["RedisExchangeHosts"].ConnectionString;
11
12 private static readonly object Locker = new object();
13 private static ConnectionMultiplexer _instance;
14 private static readonly ConcurrentDictionary<string, ConnectionMultiplexer> ConnectionCache = new ConcurrentDictionary<string, ConnectionMultiplexer>();
15
16 /// <summary>
17 /// 单例获取
18 /// </summary>
19 public static ConnectionMultiplexer Instance
20 {
21 get
22 {
23 if (_instance == null)
24 {
25 lock (Locker)
26 {
27 if (_instance == null || !_instance.IsConnected)
28 {
29 _instance = GetManager();
30 }
31 }
32 }
33 return _instance;
34 }
35 }
36
37 /// <summary>
38 /// 缓存获取
39 /// </summary>
40 /// <param name="connectionString"></param>
41 /// <returns></returns>
42 public static ConnectionMultiplexer GetConnectionMultiplexer(string connectionString)
43 {
44 if (!ConnectionCache.ContainsKey(connectionString))
45 {
46 ConnectionCache[connectionString] = GetManager(connectionString);
47 }
48 return ConnectionCache[connectionString];
49 }
50
51 private static ConnectionMultiplexer GetManager(string connectionString = null)
52 {
53 connectionString = connectionString ?? RedisConnectionString;
54 var connect = ConnectionMultiplexer.Connect(connectionString);
55
56 //注册如下事件
57 connect.ConnectionFailed += MuxerConnectionFailed;
58 connect.ConnectionRestored += MuxerConnectionRestored;
59 connect.ErrorMessage += MuxerErrorMessage;
60 connect.ConfigurationChanged += MuxerConfigurationChanged;
61 connect.HashSlotMoved += MuxerHashSlotMoved;
62 connect.InternalError += MuxerInternalError;
63
64 return connect;
65 }
66
67 #region 事件
68
69 /// <summary>
70 /// 配置更改时
71 /// </summary>
72 /// <param name="sender"></param>
73 /// <param name="e"></param>
74 private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e)
75 {
76 Console.WriteLine("Configuration changed: " + e.EndPoint);
77 }
78
79 /// <summary>
80 /// 发生错误时
81 /// </summary>
82 /// <param name="sender"></param>
83 /// <param name="e"></param>
84 private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e)
85 {
86 Console.WriteLine("ErrorMessage: " + e.Message);
87 }
88
89 /// <summary>
90 /// 重新建立连接之前的错误
91 /// </summary>
92 /// <param name="sender"></param>
93 /// <param name="e"></param>
94 private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e)
95 {
96 Console.WriteLine("ConnectionRestored: " + e.EndPoint);
97 }
98
99 /// <summary>
100 /// 连接失败 , 如果重新连接成功你将不会收到这个通知
101 /// </summary>
102 /// <param name="sender"></param>
103 /// <param name="e"></param>
104 private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e)
105 {
106 Console.WriteLine("重新连接:Endpoint failed: " + e.EndPoint + ", " + e.FailureType + (e.Exception == null ? "" : (", " + e.Exception.Message)));
107 }
108
109 /// <summary>
110 /// 更改集群
111 /// </summary>
112 /// <param name="sender"></param>
113 /// <param name="e"></param>
114 private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e)
115 {
116 Console.WriteLine("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint);
117 }
118
119 /// <summary>
120 /// redis类库错误
121 /// </summary>
122 /// <param name="sender"></param>
123 /// <param name="e"></param>
124 private static void MuxerInternalError(object sender, InternalErrorEventArgs e)
125 {
126 Console.WriteLine("InternalError:Message" + e.Exception.Message);
127 }
128
129 #endregion 事件
130 }
RedisHelper 通用操作类封
1 public class RedisHelper
2 {
3 private int DbNum { get; }
4 private readonly ConnectionMultiplexer _conn;
5 public string CustomKey;
6
7 #region 构造函数
8
9 public RedisHelper(int dbNum = 0)
10 : this(dbNum, null)
11 {
12 }
13
14 public RedisHelper(int dbNum, string readWriteHosts)
15 {
16 DbNum = dbNum;
17 _conn =
18 string.IsNullOrWhiteSpace(readWriteHosts) ?
19 RedisConnectionHelp.Instance :
20 RedisConnectionHelp.GetConnectionMultiplexer(readWriteHosts);
21 }
22
23 #region 辅助方法
24
25 private string AddSysCustomKey(string oldKey)
26 {
27 var prefixKey = CustomKey ?? RedisConnectionHelp.SysCustomKey;
28 return prefixKey + oldKey;
29 }
30
31 private T Do<T>(Func<IDatabase, T> func)
32 {
33 var database = _conn.GetDatabase(DbNum);
34 return func(database);
35 }
36
37 private string ConvertJson<T>(T value)
38 {
39 string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value);
40 return result;
41 }
42
43 private T ConvertObj<T>(RedisValue value)
44 {
45 return JsonConvert.DeserializeObject<T>(value);
46 }
47
48 private List<T> ConvetList<T>(RedisValue[] values)
49 {
50 List<T> result = new List<T>();
51 foreach (var item in values)
52 {
53 var model = ConvertObj<T>(item);
54 result.Add(model);
55 }
56 return result;
57 }
58
59 private RedisKey[] ConvertRedisKeys(List<string> redisKeys)
60 {
61 return redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();
62 }
63
64 #endregion 辅助方法
65
66 #endregion 构造函数
67 }
其中CustomKey用来表示系统前缀,AddSysCustomKey方法对每个key都进行前缀的添加处理,这里推荐大家在命名redis的key的时候最好的加上前缀,并且使用 :来分割前缀 ,这里在使用可视化工具查看的时候就比较好区分,比如我的的前缀是 Demo:test:(一般是 系统名:业务名:),然后你查看的时候你会发现整齐,好区分了很多

String类型的封装
1 #region String
2
3 #region 同步方法
4
5 /// <summary>
6 /// 保存单个key value
7 /// </summary>
8 /// <param name="key">Redis Key</param>
9 /// <param name="value">保存的值</param>
10 /// <param name="expiry">过期时间</param>
11 /// <returns></returns>
12 public bool StringSet(string key, string value, TimeSpan? expiry = default(TimeSpan?))
13 {
14 key = AddSysCustomKey(key);
15 return Do(db => db.StringSet(key, value, expiry));
16 }
17
18 /// <summary>
19 /// 保存多个key value
20 /// </summary>
21 /// <param name="keyValues">键值对</param>
22 /// <returns></returns>
23 public bool StringSet(List<KeyValuePair<RedisKey, RedisValue>> keyValues)
24 {
25 List<KeyValuePair<RedisKey, RedisValue>> newkeyValues =
26 keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(AddSysCustomKey(p.Key), p.Value)).ToList();
27 return Do(db => db.StringSet(newkeyValues.ToArray()));
28 }
29
30 /// <summary>
31 /// 保存一个对象
32 /// </summary>
33 /// <typeparam name="T"></typeparam>
34 /// <param name="key"></param>
35 /// <param name="obj"></param>
36 /// <param name="expiry"></param>
37 /// <returns></returns>
38 public bool StringSet<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
39 {
40 key = AddSysCustomKey(key);
41 string json = ConvertJson(obj);
42 return Do(db => db.StringSet(key, json, expiry));
43 }
44
45 /// <summary>
46 /// 获取单个key的值
47 /// </summary>
48 /// <param name="key">Redis Key</param>
49 /// <returns></returns>
50 public string StringGet(string key)
51 {
52 key = AddSysCustomKey(key);
53 return Do(db => db.StringGet(key));
54 }
55
56 /// <summary>
57 /// 获取多个Key
58 /// </summary>
59 /// <param name="listKey">Redis Key集合</param>
60 /// <returns></returns>
61 public RedisValue[] StringGet(List<string> listKey)
62 {
63 List<string> newKeys = listKey.Select(AddSysCustomKey).ToList();
64 return Do(db => db.StringGet(ConvertRedisKeys(newKeys)));
65 }
66
67 /// <summary>
68 /// 获取一个key的对象
69 /// </summary>
70 /// <typeparam name="T"></typeparam>
71 /// <param name="key"></param>
72 /// <returns></returns>
73 public T StringGet<T>(string key)
74 {
75 key = AddSysCustomKey(key);
76 return Do(db => ConvertObj<T>(db.StringGet(key)));
77 }
78
79 /// <summary>
80 /// 为数字增长val
81 /// </summary>
82 /// <param name="key"></param>
83 /// <param name="val">可以为负</param>
84 /// <returns>增长后的值</returns>
85 public double StringIncrement(string key, double val = 1)
86 {
87 key = AddSysCustomKey(key);
88 return Do(db => db.StringIncrement(key, val));
89 }
90
91 /// <summary>
92 /// 为数字减少val
93 /// </summary>
94 /// <param name="key"></param>
95 /// <param name="val">可以为负</param>
96 /// <returns>减少后的值</returns>
97 public double StringDecrement(string key, double val = 1)
98 {
99 key = AddSysCustomKey(key);
100 return Do(db => db.StringDecrement(key, val));
101 }
102
103 #endregion 同步方法
104
105 #region 异步方法
106
107 /// <summary>
108 /// 保存单个key value
109 /// </summary>
110 /// <param name="key">Redis Key</param>
111 /// <param name="value">保存的值</param>
112 /// <param name="expiry">过期时间</param>
113 /// <returns></returns>
114 public async Task<bool> StringSetAsync(string key, string value, TimeSpan? expiry = default(TimeSpan?))
115 {
116 key = AddSysCustomKey(key);
117 return await Do(db => db.StringSetAsync(key, value, expiry));
118 }
119
120 /// <summary>
121 /// 保存多个key value
122 /// </summary>
123 /// <param name="keyValues">键值对</param>
124 /// <returns></returns>
125 public async Task<bool> StringSetAsync(List<KeyValuePair<RedisKey, RedisValue>> keyValues)
126 {
127 List<KeyValuePair<RedisKey, RedisValue>> newkeyValues =
128 keyValues.Select(p => new KeyValuePair<RedisKey, RedisValue>(AddSysCustomKey(p.Key), p.Value)).ToList();
129 return await Do(db => db.StringSetAsync(newkeyValues.ToArray()));
130 }
131
132 /// <summary>
133 /// 保存一个对象
134 /// </summary>
135 /// <typeparam name="T"></typeparam>
136 /// <param name="key"></param>
137 /// <param name="obj"></param>
138 /// <param name="expiry"></param>
139 /// <returns></returns>
140 public async Task<bool> StringSetAsync<T>(string key, T obj, TimeSpan? expiry = default(TimeSpan?))
141 {
142 key = AddSysCustomKey(key);
143 string json = ConvertJson(obj);
144 return await Do(db => db.StringSetAsync(key, json, expiry));
145 }
146
147 /// <summary>
148 /// 获取单个key的值
149 /// </summary>
150 /// <param name="key">Redis Key</param>
151 /// <returns></returns>
152 public async Task<string> StringGetAsync(string key)
153 {
154 key = AddSysCustomKey(key);
155 return await Do(db => db.StringGetAsync(key));
156 }
157
158 /// <summary>
159 /// 获取多个Key
160 /// </summary>
161 /// <param name="listKey">Redis Key集合</param>
162 /// <returns></returns>
163 public async Task<RedisValue[]> StringGetAsync(List<string> listKey)
164 {
165 List<string> newKeys = listKey.Select(AddSysCustomKey).ToList();
166 return await Do(db => db.StringGetAsync(ConvertRedisKeys(newKeys)));
167 }
168
169 /// <summary>
170 /// 获取一个key的对象
171 /// </summary>
172 /// <typeparam name="T"></typeparam>
173 /// <param name="key"></param>
174 /// <returns></returns>
175 public async Task<T> StringGetAsync<T>(string key)
176 {
177 key = AddSysCustomKey(key);
178 string result = await Do(db => db.StringGetAsync(key));
179 return ConvertObj<T>(result);
180 }
181
182 /// <summary>
183 /// 为数字增长val
184 /// </summary>
185 /// <param name="key"></param>
186 /// <param name="val">可以为负</param>
187 /// <returns>增长后的值</returns>
188 public async Task<double> StringIncrementAsync(string key, double val = 1)
189 {
190 key = AddSysCustomKey(key);
191 return await Do(db => db.StringIncrementAsync(key, val));
192 }
193
194 /// <summary>
195 /// 为数字减少val
196 /// </summary>
197 /// <param name="key"></param>
198 /// <param name="val">可以为负</param>
199 /// <returns>减少后的值</returns>
200 public async Task<double> StringDecrementAsync(string key, double val = 1)
201 {
202 key = AddSysCustomKey(key);
203 return await Do(db => db.StringDecrementAsync(key, val));
204 }
205
206 #endregion 异步方法
207
208 #endregion String
这里说一下,StackExchange.Redis 中对对象的存储是不自带序列化和反序列化的方法,所以在ConvertJson和ConvertObj里面我是使用了JsonConvert来操作,如果需要换成其他的序列化和序列化,直接修改这两个方面就好了,另外,StackExchange.Redis 相对于ServiceStack.Redis 来说提供了异步的方法,所以这里也同样封装了异步和同步的方法。
List类型的封装
1 #region List
2
3 #region 同步方法
4
5 /// <summary>
6 /// 移除指定ListId的内部List的值
7 /// </summary>
8 /// <param name="key"></param>
9 /// <param name="value"></param>
10 public void ListRemove<T>(string key, T value)
11 {
12 key = AddSysCustomKey(key);
13 Do(db => db.ListRemove(key, ConvertJson(value)));
14 }
15
16 /// <summary>
17 /// 获取指定key的List
18 /// </summary>
19 /// <param name="key"></param>
20 /// <returns></returns>
21 public List<T> ListRange<T>(string key)
22 {
23 key = AddSysCustomKey(key);
24 return Do(redis =>
25 {
26 var values = redis.ListRange(key);
27 return ConvetList<T>(values);
28 });
29 }
30
31 /// <summary>
32 /// 入队
33 /// </summary>
34 /// <param name="key"></param>
35 /// <param name="value"></param>
36 public void ListRightPush<T>(string key, T value)
37 {
38 key = AddSysCustomKey(key);
39 Do(db => db.ListRightPush(key, ConvertJson(value)));
40 }
41
42 /// <summary>
43 /// 出队
44 /// </summary>
45 /// <typeparam name="T"></typeparam>
46 /// <param name="key"></param>
47 /// <returns></returns>
48 public T ListRightPop<T>(string key)
49 {
50 key = AddSysCustomKey(key);
51 return Do(db =>
52 {
53 var value = db.ListRightPop(key);
54 return ConvertObj<T>(value);
55 });
56 }
57
58 /// <summary>
59 /// 入栈
60 /// </summary>
61 /// <typeparam name="T"></typeparam>
62 /// <param name="key"></param>
63 /// <param name="value"></param>
64 public void ListLeftPush<T>(string key, T value)
65 {
66 key = AddSysCustomKey(key);
67 Do(db => db.ListLeftPush(key, ConvertJson(value)));
68 }
69
70 /// <summary>
71 /// 出栈
72 /// </summary>
73 /// <typeparam name="T"></typeparam>
74 /// <param name="key"></param>
75 /// <returns></returns>
76 public T ListLeftPop<T>(string key)
77 {
78 key = AddSysCustomKey(key);
79 return Do(db =>
80 {
81 var value = db.ListLeftPop(key);
82 return ConvertObj<T>(value);
83 });
84 }
85
86 /// <summary>
87 /// 获取集合中的数量
88 /// </summary>
89 /// <param name="key"></param>
90 /// <returns></returns>
91 public long ListLength(string key)
92 {
93 key = AddSysCustomKey(key);
94 return Do(redis => redis.ListLength(key));
95 }
96
97 #endregion 同步方法
98
99 #region 异步方法
100
101 /// <summary>
102 /// 移除指定ListId的内部List的值
103 /// </summary>
104 /// <param name="key"></param>
105 /// <param name="value"></param>
106 public async Task<long> ListRemoveAsync<T>(string key, T value)
107 {
108 key = AddSysCustomKey(key);
109 return await Do(db => db.ListRemoveAsync(key, ConvertJson(value)));
110 }
111
112 /// <summary>
113 /// 获取指定key的List
114 /// </summary>
115 /// <param name="key"></param>
116 /// <returns></returns>
117 public async Task<List<T>> ListRangeAsync<T>(string key)
118 {
119 key = AddSysCustomKey(key);
120 var values = await Do(redis => redis.ListRangeAsync(key));
121 return ConvetList<T>(values);
122 }
123
124 /// <summary>
125 /// 入队
126 /// </summary>
127 /// <param name="key"></param>
128 /// <param name="value"></param>
129 public async Task<long> ListRightPushAsync<T>(string key, T value)
130 {
131 key = AddSysCustomKey(key);
132 return await Do(db => db.ListRightPushAsync(key, ConvertJson(value)));
133 }
134
135 /// <summary>
136 /// 出队
137 /// </summary>
138 /// <typeparam name="T"></typeparam>
139 /// <param name="key"></param>
140 /// <returns></returns>
141 public async Task<T> ListRightPopAsync<T>(string key)
142 {
143 key = AddSysCustomKey(key);
144 var value = await Do(db => db.ListRightPopAsync(key));
145 return ConvertObj<T>(value);
146 }
147
148 /// <summary>
149 /// 入栈
150 /// </summary>
151 /// <typeparam name="T"></typeparam>
152 /// <param name="key"></param>
153 /// <param name="value"></param>
154 public async Task<long> ListLeftPushAsync<T>(string key, T value)
155 {
156 key = AddSysCustomKey(key);
157 return await Do(db => db.ListLeftPushAsync(key, ConvertJson(value)));
158 }
159
160 /// <summary>
161 /// 出栈
162 /// </summary>
163 /// <typeparam name="T"></typeparam>
164 /// <param name="key"></param>
165 /// <returns></returns>
166 public async Task<T> ListLeftPopAsync<T>(string key)
167 {
168 key = AddSysCustomKey(key);
169 var value = await Do(db => db.ListLeftPopAsync(key));
170 return ConvertObj<T>(value);
171 }
172
173 /// <summary>
174 /// 获取集合中的数量
175 /// </summary>
176 /// <param name="key"></param>
177 /// <returns></returns>
178 public async Task<long> ListLengthAsync(string key)
179 {
180 key = AddSysCustomKey(key);
181 return await Do(redis => redis.ListLengthAsync(key));
182 }
183
184 #endregion 异步方法
185
186 #endregion List
Hash类型的封装
1 #region Hash
2
3 #region 同步方法
4
5 /// <summary>
6 /// 判断某个数据是否已经被缓存
7 /// </summary>
8 /// <param name="key"></param>
9 /// <param name="dataKey"></param>
10 /// <returns></returns>
11 public bool HashExists(string key, string dataKey)
12 {
13 key = AddSysCustomKey(key);
14 return Do(db => db.HashExists(key, dataKey));
15 }
16
17 /// <summary>
18 /// 存储数据到hash表
19 /// </summary>
20 /// <typeparam name="T"></typeparam>
21 /// <param name="key"></param>
22 /// <param name="dataKey"></param>
23 /// <param name="t"></param>
24 /// <returns></returns>
25 public bool HashSet<T>(string key, string dataKey, T t)
26 {
27 key = AddSysCustomKey(key);
28 return Do(db =>
29 {
30 string json = ConvertJson(t);
31 return db.HashSet(key, dataKey, json);
32 });
33 }
34
35 /// <summary>
36 /// 移除hash中的某值
37 /// </summary>
38 /// <param name="key"></param>
39 /// <param name="dataKey"></param>
40 /// <returns></returns>
41 public bool HashDelete(string key, string dataKey)
42 {
43 key = AddSysCustomKey(key);
44 return Do(db => db.HashDelete(key, dataKey));
45 }
46
47 /// <summary>
48 /// 移除hash中的多个值
49 /// </summary>
50 /// <param name="key"></param>
51 /// <param name="dataKeys"></param>
52 /// <returns></returns>
53 public long HashDelete(string key, List<RedisValue> dataKeys)
54 {
55 key = AddSysCustomKey(key);
56 //List<RedisValue> dataKeys1 = new List<RedisValue>() {"1","2"};
57 return Do(db => db.HashDelete(key, dataKeys.ToArray()));
58 }
59
60 /// <summary>
61 ///

浙公网安备 33010602011771号