C# Redis分布式锁(基于ServiceStack.Redis)
相关的文章其实不少,我也从中受益不少,但是还是想自己梳理一下,毕竟自己写的更走心!
首先给出一个拓展类,通过拓展方法实现加锁和解锁。
注:之所以增加拓展方法,是因为合理使用拓展类(方法),可以让程序更简洁,拓展性更好。如.Net Core中新增拓展就是通过拓展类实现的,如services.AddMemoryCache();services.AddSignalR()。哎呀说多了!
using ServiceStack.Redis;
using System; namespace Redis.Core.Extension
{
/// <summary>
/// RedisNativeClient拓展类
/// </summary>
public static class RedisNativeClientExtension
{
/// <summary>
/// 锁定指定的Key
/// </summary>
/// <param name="redisClient">RedisClient 对象</param>
/// <param name="key">要锁定的Key</param>
/// <param name="expirySeconds">锁定时长 秒</param>
/// <param name="waitSeconds">锁定等待时长 秒 默认不等待</param>
/// <returns>是否锁定成功</returns>
public static bool Lock(this RedisNativeClient redisClient,string key, int expirySeconds = , double waitSeconds = )
{
int waitIntervalMs = ;//间隔等待时长 毫秒 合理配置一下 应该也会影响性能 间隔时间太长肯定是不行的
string lockKey = "lock_key:" + key; DateTime begin = DateTime.Now;
while (true)
{
if (redisClient.SetNX(lockKey, new byte[] { }) == )
{
redisClient.Expire(lockKey, expirySeconds);
return true;
} //不等待锁则返回
if (waitSeconds <= )
break; if ((DateTime.Now - begin).TotalSeconds >= waitSeconds)//等待超时
break; System.Threading.Thread.Sleep(waitIntervalMs);
}
return false;
} /// <summary>
/// 接触锁定
/// </summary>
/// <param name="redisClient">RedisClient 对象</param>
/// <param name="key">要解锁的Key</param>
/// <returns></returns>
public static long UnLock(this RedisNativeClient redisClient, string key)
{
string lockKey = "lock_key:" + key;
return redisClient.Del(lockKey);
}
}
}
实际上实现分布式锁定的关键就是以上这些代码,不过就此结束肯定不是丁哥的风格!虽然水平有限,但绝对是尽力说的明白。模拟一下使用场景吧,希望不会露怯呢。
请留意最后标注的变量值,从这几个值可以看出:
- 商品没有超卖,300个;
- 出现了秒杀应该出现的场景,有人挤进来了但是商品已经秒光了(没有被通知,茫然脸),3个;
- 还有一些人实际上可以认为根本就没排上队,请求来时已经卖光了(被正式通知),97个;
- 请求没有丢,300+3+97,共400个。
List<string> products = new List<string>();
public Startup(IConfiguration configuration)
{
Configuration = configuration; for (int i = ; i < ; i++)
{
//模拟了300个商品 实际这些商品保存在Redis里更合适 这里是为了少写一些代码了
products.Add("product_" + i.ToString());
}
//这里只是我自己封装的类库,根据配置文件创建了一个PooledRedisClientManager
//不要想复杂了 替换成你的创建方式就可以了
var clientManager = new Redis.Core.RedisClientManager(configuration);
string lockKey = "秒杀_0706"; //随意写的一个锁定用的key 这个根据业务确定用什么值就可以了 例如说商品的编号
var clientTemp = clientManager.GetClient();
clientTemp.Set("已售罄", false); //存一些数值帮你看明白运行效果
clientTemp.Set("售出数量", );
clientTemp.Set("售罄后请求次数", );
clientTemp.Set("缓存显示已售罄", );
clientTemp.Set("锁定失败", );
clientTemp.Dispose();
DateTime startTime = DateTime.Now;
bool isSellOut = false;
List<Task> tasks = new List<Task>();
for (int i = ; i < ; i++) //多线程方式 模拟很多人抢有限的商品
{
tasks.Add(Task.Factory.StartNew(() =>
{
using (var client = (ServiceStack.Redis.RedisClient)clientManager.GetClient())
{
isSellOut = client.Get<bool>("已售罄");
if (isSellOut)
{
client.Incr("缓存显示已售罄");//真实场景中 此时会在前端页面给用户提示:已经卖光啦
}
//这个Lock及下面的UnLock是个拓展方法 为了让大家看清楚逻辑 所以当静态方法用了
//3 是锁定时间3秒;1 是进行锁定等待时间1秒,这个值越小 锁定失败的几率就会越大。
else if (Redis.Core.Extension.RedisNativeClientExtension.Lock(client, lockKey, , ))
{
if (products.Count == )
{
//这个很重要 这里标记已经卖完 就不会再做后面的逻辑了
//能避免后面几十万的不必要访问(如果是电商的话)
client.Set("已售罄", true);
//秒杀场景中 肯定会有大量用户请求执行到这个环节 所以采用isSellOut
client.Incr("售罄后请求次数");
}
else
{
client.Incr("售出数量");
products.RemoveAt();//这里只是最简单的模拟了生成订单、减少库存量
}
//As you know,这里要解锁得啦
Redis.Core.Extension.RedisNativeClientExtension.UnLock(client, lockKey);
}
else
{
client.Incr("锁定失败");
}
}
}));
}
Task.WaitAll(tasks.ToArray());//等待所有人抢完(多线程的知识点这里就不讲了哟) 然后才获取下面的结果
var 售出数量 = clientManager.GetClient().Get<string>("售出数量"); //
var 售罄后请求次数 = clientManager.GetClient().Get<string>("售罄后请求次数"); //
var 缓存显示已售罄 = clientManager.GetClient().Get<string>("缓存显示已售罄"); //
var 锁定失败 = clientManager.GetClient().Get<string>("锁定失败"); //
}
模拟秒杀场景代码
真实秒杀场景必然要比以上示例要复杂的多,涉及到商品信息的来源(不要走DB哟),订单的创建等等,这时又会涉及到MQ、缓存预热等更多的问题。我这里就是抛转引玉,希望对大家有些许帮助。
努力工作 认真生活 持续学习 以勤补拙
C# Redis分布式锁(基于ServiceStack.Redis)的更多相关文章
- 基于SpringBoot AOP面向切面编程实现Redis分布式锁
基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式 ...
- 关于Redis分布式锁这一篇应该是讲的最好的了,先收藏起来再看!
前言 在Java并发编程中,我们通常使用到synchronized .Lock这两个线程锁,Java中的锁,只能保证对同一个JVM中的线程有效.而在分布式集群环境,这个时候我们就需要使用到分布式锁. ...
- 手撕redis分布式锁,隔壁张小帅都看懂了!
前言 上一篇老猫和小伙伴们分享了为什么要使用分布式锁以及分布式锁的实现思路原理,目前我们主要采用第三方的组件作为分布式锁的工具.上一篇运用了Mysql中的select ...for update实现了 ...
- 七种方案!探讨Redis分布式锁的正确使用姿势
前言 日常开发中,秒杀下单.抢红包等等业务场景,都需要用到分布式锁.而Redis非常适合作为分布式锁使用.本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式.如果有不正确的地方,欢迎大家 ...
- Redis分布式锁 (图解-秒懂-史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
- python redis分布式锁改进
0X01 python redis分布式锁通用方法 REDIS分布式锁实现的方式:SETNX + GETSET 使用Redis SETNX 命令实现分布式锁 python 版本实现上述思路(案例1) ...
- RedLock.Net - 基于Redis分布式锁的开源实现
工作中,经常会遇到分布式环境中资源访问冲突问题,比如商城的库存数量处理,或者某个事件的原子性操作,都需要确保某个时间段内只有一个线程在访问或处理资源. 因此现在网上也有很多的分布式锁的解决方案,有数据 ...
- 基于Redis分布式锁(获取锁及解锁)
目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency).可用性( ...
- 基于 Redis 分布式锁
1.主流分布式锁实现方案 基于数据库实现分布式锁 基于缓存(redis 等) 基于 Zookeeper 2.根据实现方式分类 : 类 CAS 自旋式分布式锁:询问的方式,类似 java 并发编程中的线 ...
- Redis分布式锁,基于StringRedisTemplate和基于Lettuce实现setNx
使用redis分布式锁,来确保多个服务对共享数据操作的唯一性一般来说有StringRedisTemplate和RedisTemplate两种redis操作模板. 根据key-value的类型决定使用哪 ...
随机推荐
- hdu 2037 这个夏天不AC (java)
问题: 这个题为项贪心算法.我们的想法是在第一时间每个周期根据结束排序(按结束越早穿越,更多的程序), 然后从第一个节目开始.假设下一个节目的开始时间大于一个节目的开始时间,是进行程序,依次递推. 输 ...
- JS超链接动态显示图片
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...
- WPF中INotifyPropertyChanged用法与数据绑定
在WPF中进行数据绑定的时候常常会用到INotifyPropertyChanged接口来进行实现,下面来看一个INotifyPropertyChanged的案例. 下面定义一个Person类: usi ...
- 用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机
原文:用MVVM模式开发中遇到的零散问题总结(5)--将动态加载的可视元素保存为图片的控件,Binding刷新的时机 在项目开发中经常会遇到这样一种情况,就是需要将用户填写的信息排版到一张表单中,供打 ...
- Delphi多线程下的ADO编程
前言: 几个月前接到一个任务:将一后台程序访问数据库的方式从BDE改为ADO,原因是由于业务量的增加,通过BDE不论是向数据库写入数据还是从数据库中读出数据的速度都变得无法忍受,大家都知道ADO在数据 ...
- Windows10 【系统周期表】【系统下载表】【大型软件表】
系统周期表 商用名称 商用英文名 代号 版本 系统版本 上市日期 服务周期 备注 Windows 10 无 Threshold 1 1507 10.0.10240.17443 2015.07.29 2 ...
- 操作XML文档遇到的XMLNS问题及解决方法 (C# 和 PHP)
原文:操作XML文档遇到的XMLNS问题及解决方法 (C# 和 PHP) 不管是用 PHP 还是 C#, 在操作 XML 的时候我们除了一个节点一个节点去取值之外, 还有一个非常方便的表达式, 就是 ...
- 零元学Expression Blend 4 – Chapter 43 如何指定Childwindow PopUp位置
原文:零元学Expression Blend 4 – Chapter 43 如何指定Childwindow PopUp位置 有网友询问我有关Childwindow是否能指定弹出位置? 其实只要透过小小 ...
- Android零基础入门第81节:Activity数据传递
在Android开发中,经常要在Activity之间传递数据.前面也学习了Activity和Intent相关基础,接下来一起来学习Activity的数据传递. 一.简介 通过前面的学习知道,Inten ...
- 论文阅读计划1(Benchmarking Streaming Computation Engines: Storm, Flink and Spark Streaming & An Enforcement of Real Time Scheduling in Spark Streaming & StyleBank: An Explicit Representation for Neural Ima)
Benchmarking Streaming Computation Engines: Storm, Flink and Spark Streaming[1] 简介:雅虎发布的一份各种流处理引擎的基准 ...