封装的 Redis 操作类名为 RedisHandle, 如下代码块 (只展示部分代码), 它的特点:
1) 使用连接池管理连接, 见代码中的 PooledClientManager 属性. 如果不用连接池, 而是代码直接 RedisClient client = new RedisClient("localhost", 6379, "password"); 去获取一个连接实例操作, 那么当 Redis 操作频繁时, 代价很大, 不可行.
2) 支持读写分离的 Redis 服务端 (如果你只用一个 Redis 服务端, 那么读写服务端连接字符串一样即可).
3) 操作 Redis 时, 自动切换读写 Redis 连接实例, 见代码中的 GetRedisClient 函数, 所有写操作取 "写连接实例"PooledClientManager.GetClient(), 所有读操作取 "读连接实例"PooledClientManager.GetReadOnlyClient().
注意: 如果你读写是两个做了主从复制的 Redis 服务端, 那么要考虑主从复制是否有延迟, 是否有一些读操作要求实时数据, 如果是, 那么需要在 GetXX 读数据时用写连接实例. 这时候, 可以改写此 GetXX 函数, 可在函数参数末尾增加 bool? isReadOnly = null 带默认值的参数, 即支持外部调用指定用哪种连接实例操作. 这种情况一般是系统把 Redis 当作一个 NoSql 数据库; 而更多时候我们系统是把 Redis 当作一个缓存, 不需要做主从复制, 读写连接实例指向的是同一个 Redis 服务端, 当系统比较大时可能会用到缓存集群 (比如一致性哈希缓存等).
4) 后继如果 Redis 需要做一致性哈希等集群, 那么可以实例化多个 RedisHandle 实例, 然后撰写算法来取相应的 RedisHandle 实例.
- namespace NetDh.RedisUtility
- {
- /*
- * 一个 RedisHandle 实例对应一个 Redis 服务端或者一组主从复制 Redis 服务端.
- * 如果 Redis 需要做一致性哈希等集群, 则要自己撰写算法来取相应的 RedisHandle 实例.
- */
- /// <summary>
- /// Redis 操作类
- /// </summary>
- public class RedisHandle
- {
- /// <summary>
- /// Redis 连接池管理实例
- /// </summary>
- public PooledRedisClientManager PooledClientManager;
- /// <summary>
- /// 构造函数
- /// </summary>
- /// <param name="poolManager"></param>
- public RedisHandle()
- {
- #region 此代码为创建 "连接池示例", 配置信息直接用静态类 RedisClientConfig1 承载, 你也可以选择用配置文件承载
- var config = new RedisClientManagerConfig
- {
- AutoStart = true,
- MaxWritePoolSize = RedisClientConfig1.MaxWritePoolSize,
- MaxReadPoolSize = RedisClientConfig1.MaxReadPoolSize,
- DefaultDb = RedisClientConfig1.DefaultDb,
- };
- // 如果你只用到一个 Redis 服务端, 那么配置读写时就指定一样的连接字符串即可.
- PooledClientManager = new PooledRedisClientManager(RedisClientConfig1.ReadWriteServers
- , RedisClientConfig1.ReadOnlyServers, config)
- {
- ConnectTimeout = RedisClientConfig1.ConnectTimeout,
- SocketSendTimeout = RedisClientConfig1.SendTimeout,
- SocketReceiveTimeout = RedisClientConfig1.ReceiveTimeout,
- IdleTimeOutSecs = RedisClientConfig1.IdleTimeOutSecs,
- PoolTimeout = RedisClientConfig1.PoolTimeout
- };
- #endregion
- }
- /// <summary>
- /// 构造函数
- /// </summary>
- /// <param name="poolManager"></param>
- public RedisHandle(PooledRedisClientManager poolManager)
- {
- // 也可外部传入自己创建的 PooledRedisClientManager 对象
- PooledClientManager = poolManager;
- }
- /// <summary>
- /// 获取 Redis 客户端连接实例, 有连接池管理.
- /// </summary>
- /// <param name="isReadOnly"> 是否取只读连接. Get 操作一般是读, Set 操作一般是写 </param>
- /// <param name="db"> 一个 Redis 服务端默认有 16 个数据库, 默认都是用第 0 个数据库. 如果需要切换数据库, 则传入 db 值 (0~15)</param>
- /// <returns></returns>
- public RedisClient GetRedisClient(bool isReadOnly = false, int db = 0)
- {
- RedisClient result;
- if (!isReadOnly)
- {
- //RedisClientManager.GetCacheClient() 会返回一个新实例, 而且只提供一小部分方法, 它的作用是帮你判断是否用写实例还是读实例
- result = PooledClientManager.GetClient() as RedisClient;
- }
- else
- {
- // 如果你读写是两个做了主从复制的 Redis 服务端, 那么要考虑主从复制是否有延迟. 有一些读操作是否是即时的, 需要在写实例中获取.
- result = PooledClientManager.GetReadOnlyClient() as RedisClient;
- }
- if (db> 0)
- result.ChangeDb(db);
- return result;
- }
- #region 存储单值 key-value, 其中 value 是 string, 使用时如果 value 是 int, 可以把比如 int 转成 string 存储
- public void SetValue(string key, string value, int expirySeconds = -1)
- {
- using (RedisClient redisClient = GetRedisClient())
- {
- //redisClient.SetEntry(key, value, expireIn);
- if (expirySeconds == -1)
- {
- redisClient.SetValue(key, value);
- }
- else
- {
- redisClient.SetValue(key, value, new TimeSpan(0, 0, 0, expirySeconds));
- }
- }
- }
- public string GetValue(string key)
- {
- using (RedisClient redisClient = GetRedisClient(true))
- {
- var val = redisClient.GetValue(key);
- return val;
- }
- }
- public bool Remove(string key)
- {
- ...
5) 在 GetRedisClient 函数中还开出一个参数 int db, 使用时: if (db> 0) result.ChangeDb(db); ,ChangeDb 是切换 Redis 数据库 (Redis 默认有 16 个数据库, 见 Redis-server.exe 目录下的 Redis.conf 配置文件中的 "databases 16"). 我们一般默认都是用第 0 个数据库, 如果需要切换数据库, 则传入 db 值 (0~15). 我这边一般不会用到切换数据库的需求, 如果你的系统会经常用到, 则可在 GetXX 和 SetXX 函数中加入 "int db=0" 有默认值的参数.
为了说明一个 Redis 服务端有多个数据库以及数据库之间的切换, 做个小示例, 如下图, 我在 Redis 的第 0 个数据库存放了键值对数据 "test2:1", 当我切到第 1 个数据库 ChangeDb(1) 时, GetValue("test2") 返回的是 null, 当切回第 0 个数据库时, 就取到 1 的值.
现在用命令登录 Redis 再演示一遍这个过程, 如下图:
6)RedisHandle 操作类包含的操作, 大致如下图, Redis 支持的数据类型比 Memcache 多, 而且很实用, 如果你的系统存取缓存会涉及比较复杂的逻辑, 推荐使用 Redis,Memcache 能的 Redis 都能.
完整的源码请参考:
此工具类已经并到我的 NetDh 框架项目中, NetDh 框架码云地址: https://gitee.com/donghan/NetDh-Framework
2.ServiceStack.Redis 破解
我这边封装的是 ServiceStack.Redis 最新版本 5.7.0, 它在 4.0 版本之后就商业化, 有做限制: 每小时只能有 6000 次的 Redis 访问. 网上有对 ServiceStack.Redis 和 StackExchange.Reids 进行比较, 结果是前者性能比较好, 不管真假, 我是 ServiceStack.Redis 3.x 就开始用它了, 一如既往继续用呗, 有限制就破解呗.
步骤:
1) 限制 6000 次是在 ServiceStack.Text.dll 中, 而且在两个地方, 用 ILSpy 打开 ServiceStack.Text.dll, 在搜索栏输入 "RedisRequestPerHour", 可以看到 RedisRequestPerHour=6000 的限制, 如下图 (第 1 步你可不做, 看看就好):
再搜索 "AssertValidUsage", 发现另一个地方的 6000 次限制, 如下图:
2) 下载一个十六进制编辑器, 我网上找的是 wxMEdit 工具 (下载页面: http://wxmedit.github.io/downloads.html).
3) 先备份 ServiceStack.Text.dll, 用十六进制编辑器打开 ServiceStack.Text.dll.
分析: 6000 转换成字节形式是 70 17 00 00(虽然 6000 的 16 进制是 00001770),int 的最大值 2147483647 转换成字节形式是 FF FF FF 7F, 所以只要把 70 17 00 00 替换成 FF FF FF 7F 即可.
如下图, 替换之前点了 "查找下一个" 发现全局就两个地方, 那就确定是要修改的值, 然后点击 "替换" 两次, ctrl+s 保存文件, dll 修改完成.
4) 再用 ILSpy 看这两个值, 已经修改了, 如下图 (第 4 步你也可不做, 看看就好):
5) 把修改的 dll 覆盖原来 dll, 最好在 IDE 中把原来的引用移除, 重新添加引用一次, 以防有缓存执行的还是旧的 dll. 编写如下代码测试:
覆盖 dll 之前会报 6000 限制, 覆盖之后输出 ok 正常:
完美, 点赞!
来源: https://www.cnblogs.com/michaeldonghan/p/11632931.html