前言

公司的项目以前一直使用 CSRedis 这个类库来操作 Redis,最近增加了一些新功能,会存储一些比较大的数据,内测的时候发现其中有两台服务器会莫名的报错 Unexpected response type: Status (expecting Bulk)Connection was not opened,最后定位到问题是 Redis 写入和读取数据的时候发生的错误,弄了两台新服务器重新部署还是没有解决,在 GitHub 上向作者发了 issues,作者说升级类库可以解决,尝试了一下也没有解决,无奈之下只好写了个小程序用 StackExchange.Redis 在服务器上做读写测试,发现没有任何问题,防止耽误上线只好换成了 StackExchange.Redis,经过两天内部测试,一切操作均未发现异常。

StackExchange.Redis 封装

RedisClient 类:

    /// <summary>
/// 封装 Redis 相关操作的方法类。
/// </summary>
public class RedisClient : IRedisClient
{
private readonly IConnectionMultiplexer _connectionMultiplexer;
private readonly IDatabase _database; /// <summary>
/// 初始化 <see cref="RedisClient"/> 类的新实例。
/// </summary>
/// <param name="connectionMultiplexer">连接多路复用器。</param>
public RedisClient(IConnectionMultiplexer connectionMultiplexer)
{
_connectionMultiplexer = connectionMultiplexer;
if (_connectionMultiplexer != null && _connectionMultiplexer.IsConnected)
{
_database = _connectionMultiplexer.GetDatabase();
}
else
{
throw new Exception("Redis is not Connected");
} } #region 同步方法... /// <summary>
/// 添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
public bool Set(string key, string value, TimeSpan? expiry = null)
{
return _database.StringSet(key, value, expiry);
} /// <summary>
/// 添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
public bool Set(string key, string value, int seconds)
{
TimeSpan expiry = TimeSpan.FromSeconds(seconds);
return _database.StringSet(key, value, expiry);
} /// <summary>
/// 添加一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
public bool Set<T>(string key, T value, TimeSpan? expiry = null)
{
var data = JsonConvert.SerializeObject(value);
return _database.StringSet(key, data, expiry);
} /// <summary>
/// 添加一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
public bool Set<T>(string key, T value, int seconds)
{
TimeSpan expiry = TimeSpan.FromSeconds(seconds);
var data = JsonConvert.SerializeObject(value);
return _database.StringSet(key, data, expiry);
} /// <summary>
/// 获取一个对象。
/// </summary>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
public T Get<T>(string key)
{
string json = _database.StringGet(key);
if (string.IsNullOrWhiteSpace(json))
{
return default(T);
}
T entity = JsonConvert.DeserializeObject<T>(json);
return entity;
} /// <summary>
/// 获取一个字符串对象。
/// </summary>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
public string Get(string key)
{
return _database.StringGet(key);
} /// <summary>
/// 删除一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <returns>返回是否执行成功。</returns>
public bool Delete(string key)
{
return _database.KeyDelete(key);
} /// <summary>
/// 返回键是否存在。
/// </summary>
/// <param name="key">键。</param>
/// <returns>返回键是否存在。</returns>
public bool Exists(string key)
{
return _database.KeyExists(key);
} /// <summary>
/// 设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
public bool SetExpire(string key, TimeSpan? expiry)
{
return _database.KeyExpire(key, expiry);
} /// <summary>
/// 设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
public bool SetExpire(string key, int seconds)
{
TimeSpan expiry = TimeSpan.FromSeconds(seconds);
return _database.KeyExpire(key, expiry);
} #endregion #region 异步方法... /// <summary>
/// 异步添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
public async Task<bool> SetAsync(string key, string value, TimeSpan? expiry = null)
{
return await _database.StringSetAsync(key, value, expiry);
} /// <summary>
/// 异步添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
public async Task<bool> SetAsync(string key, string value, int seconds)
{
TimeSpan expiry = TimeSpan.FromSeconds(seconds);
return await _database.StringSetAsync(key, value, expiry);
} /// <summary>
/// 异步添加一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <returns>返回是否执行成功。</returns>
public async Task<bool> SetAsync<T>(string key, T value)
{
var data = JsonConvert.SerializeObject(value);
return await _database.StringSetAsync(key, data);
} /// <summary>
/// 异步获取一个对象。
/// </summary>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
public async Task<T> GetAsync<T>(string key)
{
string json = await _database.StringGetAsync(key);
if (string.IsNullOrWhiteSpace(json))
{
return default(T);
}
T entity = JsonConvert.DeserializeObject<T>(json);
return entity;
} /// <summary>
/// 异步获取一个字符串对象。
/// </summary>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
public async Task<string> GetAsync(string key)
{
return await _database.StringGetAsync(key);
} /// <summary>
/// 异步删除一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <returns>返回是否执行成功。</returns>
public async Task<bool> DeleteAsync(string key)
{
return await _database.KeyDeleteAsync(key);
} /// <summary>
/// 异步设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
public async Task<bool> SetExpireAsync(string key, int seconds)
{
TimeSpan expiry = TimeSpan.FromSeconds(seconds);
return await _database.KeyExpireAsync(key, expiry);
} /// <summary>
/// 异步设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
public async Task<bool> SetExpireAsync(string key, TimeSpan? expiry)
{
return await _database.KeyExpireAsync(key, expiry);
} #endregion #region 分布式锁... /// <summary>
/// 分布式锁 Token。
/// </summary>
private static readonly RedisValue LockToken = Environment.MachineName; /// <summary>
/// 获取锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>是否已锁。</returns>
public bool Lock(string key, int seconds)
{
return _database.LockTake(key, LockToken, TimeSpan.FromSeconds(seconds));
} /// <summary>
/// 释放锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <returns>是否成功。</returns>
public bool UnLock(string key)
{
return _database.LockRelease(key, LockToken);
} /// <summary>
/// 异步获取锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>是否成功。</returns>
public async Task<bool> LockAsync(string key, int seconds)
{
return await _database.LockTakeAsync(key, LockToken, TimeSpan.FromSeconds(seconds));
} /// <summary>
/// 异步释放锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <returns>是否成功。</returns>
public async Task<bool> UnLockAsync(string key)
{
return await _database.LockReleaseAsync(key, LockToken);
} #endregion
}

IRedisClient 类:

    /// <summary>
/// 封装 Redis 相关操作的方法。
/// </summary>
public interface IRedisClient
{
/// <summary>
/// 添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
bool Set(string key, string value, TimeSpan? expiry = null); /// <summary>
/// 添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
bool Set(string key, string value, int seconds); /// <summary>
/// 添加一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
bool Set<T>(string key, T value, TimeSpan? expiry = null); /// <summary>
/// 添加一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
bool Set<T>(string key, T value, int seconds); /// <summary>
/// 获取一个对象。
/// </summary>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
T Get<T>(string key); /// <summary>
/// 获取一个字符串对象。
/// </summary>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
string Get(string key); /// <summary>
/// 删除一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <returns>返回是否执行成功。</returns>
bool Delete(string key); /// <summary>
/// 返回键是否存在。
/// </summary>
/// <param name="key">键。</param>
/// <returns>返回键是否存在。</returns>
bool Exists(string key); /// <summary>
/// 设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
bool SetExpire(string key, TimeSpan? expiry); /// <summary>
/// 设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
bool SetExpire(string key, int seconds); /// <summary>
/// 异步添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
Task<bool> SetAsync(string key, string value, TimeSpan? expiry = null); /// <summary>
/// 异步添加一个字符串对象。
/// </summary>
/// <param name="key">键。</param>
/// <param name="value">值。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
Task<bool> SetAsync(string key, string value, int seconds); /// <summary>
/// 异步添加一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="value">值。</param>
/// <returns>返回是否执行成功。</returns>
Task<bool> SetAsync<T>(string key, T value); /// <summary>
/// 异步获取一个对象。
/// </summary>
/// <typeparam name="T">对象的类型。</typeparam>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
Task<T> GetAsync<T>(string key); /// <summary>
/// 异步获取一个字符串对象。
/// </summary>
/// <param name="key">值。</param>
/// <returns>返回对象的值。</returns>
Task<string> GetAsync(string key); /// <summary>
/// 异步删除一个对象。
/// </summary>
/// <param name="key">键。</param>
/// <returns>返回是否执行成功。</returns>
Task<bool> DeleteAsync(string key); /// <summary>
/// 异步设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>返回是否执行成功。</returns>
Task<bool> SetExpireAsync(string key, int seconds); /// <summary>
/// 异步设置一个键的过期时间。
/// </summary>
/// <param name="key">键。</param>
/// <param name="expiry">过期时间(时间间隔)。</param>
/// <returns>返回是否执行成功。</returns>
Task<bool> SetExpireAsync(string key, TimeSpan? expiry); #region 分布式锁... /// <summary>
/// 获取锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>是否已锁。</returns>
bool Lock(string key, int seconds); /// <summary>
/// 释放锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <returns>是否成功。</returns>
bool UnLock(string key); /// <summary>
/// 异步获取锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <param name="seconds">过期时间(秒)。</param>
/// <returns>是否成功。</returns>
Task<bool> LockAsync(string key, int seconds); /// <summary>
/// 异步释放锁。
/// </summary>
/// <param name="key">锁名称。</param>
/// <returns>是否成功。</returns>
Task<bool> UnLockAsync(string key); #endregion }

服务注册和配置

        services.AddSingleton<IRedisClient, RedisClient>();
services.AddSingleton<IConnectionMultiplexer, ConnectionMultiplexer>();
services.AddSingleton<IConnectionMultiplexer>(a =>
{
ConfigurationOptions options = ConfigurationOptions.Parse(redisConfig.url);
options.Password = redisConfig.pass;
string configuration = "{0},$UNLINK=,abortConnect=false,defaultDatabase={1},ssl=false,ConnectTimeout={2},allowAdmin=true,connectRetry={3},password={4}";
ConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect(string.Format(configuration, redisConfig.url, 0, 1800, 3, redisConfig.pass));
return connectionMultiplexer;
});

使用

直接在使用的类里构造注入就可以使用了。

    [SwaggerTag("User", Description = "用户管理")]
[Authorize]
public class UserController : ApiBaseController
{
readonly IUserService _userService;
readonly IUserRoleService _userRoleService;
private readonly ILogger _logger;
private readonly IMapper _mapper;
private readonly IRedisClient _redisClient;
private const string keyPrefix = "token:"; public UserController(IUserService userService, IUserRoleService userRoleService, ILogger<UserController> logger, IMapper mapper, IRedisClient redisClient)
{
_userService = userService;
_logger = logger;
_userRoleService = userRoleService;
_mapper = mapper;
_redisClient = redisClient;
}
/// <summary>
/// 设置 Redis 数据。
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
[HttpGet]
[Route("redis/set")]
public ApiResult<Dictionary<string, bool>> SetRedisData(string key, string value)
{
TimeSpan timeSpan = TimeSpan.FromSeconds(7 * 24 * 60 * 60);
var flag = _redisClient.Set(key, value, timeSpan);
Dictionary<string, bool> res = new Dictionary<string, bool>
{
{ "ok", flag }
};
return ApiResult<Dictionary<string, bool>>.Current.UpdateSuccess(res);
} }

关于锁的使用参考了 axel10 大神的文章,需要注意的是一定要禁用 UNLINK,不然会报 StackExchange.Redis.RedisServerException:“EXECABORT Transaction discarded because of previous errors.” 这个错误,UNLINK 需要 Redis 4.0 以上的版本才支持。

DotNet Core 使用 StackExchange.Redis 简单封装和实现分布式锁的更多相关文章

  1. Redis中是如何实现分布式锁的?

    分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面试考点是,你对Redis使用熟悉吗?Redis中是如何实现分布式锁的. 要点 Red ...

  2. 【spring boot】【redis】spring boot基于redis的LUA脚本 实现分布式锁

    spring boot基于redis的LUA脚本 实现分布式锁[都是基于redis单点下] 一.spring boot 1.5.X 基于redis 的 lua脚本实现分布式锁 1.pom.xml &l ...

  3. python使用redis实现协同控制的分布式锁

    python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...

  4. Redis的“假事务”与分布式锁

    关注公众号:CoderBuff,回复"redis"获取<Redis5.x入门教程>完整版PDF. <Redis5.x入门教程>目录 第一章 · 准备工作 第 ...

  5. 使用数据库、Redis、ZK分别实现分布式锁!

    分布式锁三种实现方式: 基于数据库实现分布式锁: 基于缓存(Redis等)实现分布式锁: 基于Zookeeper实现分布式锁: 基于数据库实现分布式锁 悲观锁 利用select - where - f ...

  6. 基于redis集群实现的分布式锁,可用于秒杀商品的库存数量管理,有測试代码(何志雄)

    转载请标明出处. 在分布式系统中,常常会出现须要竞争同一资源的情况,本代码基于redis3.0.1+jedis2.7.1实现了分布式锁. redis集群的搭建,请见我的另外一篇文章:<>& ...

  7. 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

  8. 分布式缓存技术redis系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

  9. redis系列之5----redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

随机推荐

  1. Apsara Clouder云计算技能认证:云数据库管理与数据迁移

    一.课程介绍 二.云数据库的简介及使用场景 1.云数据库简介 1.1特点: 用户按存储容量和带宽的需求付费 可移植性 按需扩展 高可用性(HA) 1.2阿里云云数据库 RDS 稳定可靠,可弹性伸缩的在 ...

  2. MySQL_备份

    Mysql 的备份 前情了解与小试 三种级别的备份与恢复数据:​1.表级别备份:mysqldump [OPTIONS] database [tables]mysqldump -p密码 库名 表名 &g ...

  3. VBA引用管理工具(可用于32、64位Office,Win7 Win10均可)

    VBAReferenceManager安装和使用方法,操作请看动态图: 下载地址: VBAReferenceManager.zip

  4. git相关学习地址

    https://git-scm.com/book/zh/v2    这篇文章写得不错,值得一读

  5. MyEclipse10安装properties文件插件

    安装步骤 1.下载PropertiesEditor插件 http://propedit.sourceforge.jp/index_en.html 2.解压出features.plugins文件 3.在 ...

  6. Nginx 反向代理,IP、端口,项目路径变化的问题

    这两天在云上部署公司项目,涉及到nginx反向代理,在部署完成测试,发现在下载文件的时候,无法下载,提示链接被拒绝. 假设nginx代理地址: http://121.53.21.188:9012/we ...

  7. ORs-5-OR Subgenomes Variation among Birds, Sea Turtle and Alligator

    OR Subgenomes Variation among Birds, Sea Turtle and Alligator 由 该图数据计算每种鸟的relative percentage,得到下图: ...

  8. xml_class来自 phpcms

    <?php class xml{ var $parser; var $document; var $stack; var $data; var $last_opened_tag; var $is ...

  9. hadoop-2.5.2 源码学习1

  10. [LC] 122. Best Time to Buy and Sell Stock II

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...