C# Redis分布式锁(RedLock) - 多节点
Redis单节点的分布式锁只需要注意三点就可以了:
1.加锁并设置锁的过期时间必须是原子操作;
2.锁的value值必须要有唯一性;
3.释放锁的时候要验证其value值,不是自己加的锁不能释放.
但是单节点分布式锁最大的缺点就是,它只作用在一个Redis节点上,如果该节点挂了,那就挂了.
那可不可以通过哨兵机制来保证高可用呢?
答案是不行.
因为Redis在进行主从复制的时候是异步的.
假设 clientA 拿到锁后,在 master 还没同步到 slave 时,master 发生了故障,这时候 salve 升级为 master,导致锁丢失.
RedLock 的思想是:假设有5个Redis节点.这些节点完全相互独立,不存在主从或者集群机制,都是 master.并且这5个Redis实例运行在5台机器上,这样保证他们不会同时宕掉.
客户端应该按照以下操作来获取锁:
1.获取当前时间戳,假设是T1.
2.依次尝试从这5个Redis实例获取锁.当客户端向Redis请求获取锁时,客户端应该设置超时时间,并且这个超时时间应该小于锁的失效时间.比如你的锁自动失效时间为10秒,则超时时间应该在5-50毫秒之间.这样可以避免Redis已经挂掉的情况下,客户端还在等待响应结果.如果Redis没有在规定时间内响应,客户端应该尽快尝试去另外一个Redis实例请求获取锁.
3.请求完所有的Redis节点后,只有满足如下两点,才算真正的获取到锁:
1)当前时间 - T1 的时间差小于锁的过期时间.比如T1=00:00:00,然后从5个Redis节点都拿到了锁,当前时间是 00:00:05,也就是说获取锁一共用了5秒钟.假设锁的过期时间是3秒,那么这次获取锁的操作就算失败了.
2)从(N/2+1)个Redis节点都获取到锁.这个很好理解,5个节点,你拿2个,我拿2个,到底算谁的?
总结一句话就是:从开始获取锁计时,只要在锁的过期时间内成功获取到一半以上的锁便算成功,否则算失败.
4.当客户端获取到了锁,锁的真正有效时间 = 锁的过期时间 - 获取锁所使用的时间(也就是第3步计算出来的时间).
5.如果客户端由于某些原因(比如获取锁的实例个数小于N/2+1,或者已经超过了有效时间),没有获取到锁,客户端便会在所有的Redis实例上进行解锁(即使某些Redis实例根本就没有加锁成功),因为可能已经获取了小于 N/2+1个锁,必须释放掉,否则会影响其他客户端获取锁.
关于是否启动AOF永久存储,需要有所取舍.
1.永久启动,由于Redis的过期机制是按照unix时间戳走的,所以当我们重启Redis后,依然会按照规定的时间过期.但是永久启动对性能有一定影响;
2.采用默认的1秒1次.如果在1秒内断电,会导致数据丢失,这时候如果立刻重启会导致锁的互斥性实效.
所以有效的解决方案是,采用AOF,1秒1次,不管什么原因宕机后,等待一定时间再重启.这个时间就是锁的过期时间.
Demo:
安装官方提供的 RedLock.net
Startup:
- public class Startup
- {
- private RedLockFactory _redLockFactory;
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddControllers();
- var endPoints = new List<RedLockEndPoint>
- {
- new DnsEndPoint("127.0.0.1", 6379),
- new DnsEndPoint("127.0.0.1", 6380),
- new DnsEndPoint("127.0.0.1", 6381)
- };
- _redLockFactory = RedLockFactory.Create(endPoints);
- services.AddSingleton(typeof(IDistributedLockFactory), _redLockFactory);
- }
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
- //应用程序结束时释放,因为不是容器创建的对象
- applicationLifetime.ApplicationStopping.Register(() =>
- {
- _redLockFactory.Dispose();
- });
- app.UseRouting();
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
- }
- }
测试api:
- [ApiController]
- public class ValuesController : ControllerBase
- {
- private static int _stock = 10;
- private readonly IDistributedLockFactory _distributedLockFactory;
- public ValuesController(IDistributedLockFactory distributedLockFactory)
- {
- _distributedLockFactory = distributedLockFactory;
- }
- [Route("lockTest")]
- [HttpGet]
- public async Task<int> DistributedLockTest()
- {
- // resource 锁定的资源
- var resource = "the-thing-we-are-locking-on";
- // expiryTime 锁的过期时间
- var expiry = TimeSpan.FromSeconds(5);
- // waitTime 等待时间
- var wait = TimeSpan.FromSeconds(1);
- // retryTime 等待时间内,多久重试一次
- var retry = TimeSpan.FromMilliseconds(250);
- using (var redLock = await _distributedLockFactory.CreateLockAsync(resource, expiry, wait, retry))
- {
- if (redLock.IsAcquired)
- {
- // 模拟执行业务逻辑
- await Task.Delay(new Random().Next(100, 500));
- if (stock > 0)
- {
- stock--;
- return stock;
- }
- return stock;
- }
- Console.WriteLine($"{DateTime.Now} : 获取锁失败");
- }
- return -99;
- }
- }
测试控制台:
- static void Main(string[] args)
- {
- HttpClient client = new HttpClient();
- var result = Parallel.For(0, 20, (i) =>
- {
- var stopwatch = new Stopwatch();
- stopwatch.Start();
- var response = client.GetAsync($"http://localhost:5000/locktest").Result;
- stopwatch.Stop();
- var data = response.Content.ReadAsStringAsync().Result;
- Console.WriteLine($"ThreadId:{Thread.CurrentThread.ManagedThreadId}, Result:{data}, Time:{stopwatch.ElapsedMilliseconds}");
- });
- client.Dispose();
- Console.ReadKey();
- }
测试结果:
C# Redis分布式锁(RedLock) - 多节点的更多相关文章
- 七种方案!探讨Redis分布式锁的正确使用姿势
前言 日常开发中,秒杀下单.抢红包等等业务场景,都需要用到分布式锁.而Redis非常适合作为分布式锁使用.本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式.如果有不正确的地方,欢迎大家 ...
- Redlock(redis分布式锁)原理分析
Redlock:全名叫做 Redis Distributed Lock;即使用redis实现的分布式锁: 使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击) ...
- Redlock:Redis分布式锁最牛逼的实现
普通实现 说道Redis分布式锁大部分人都会想到:setnx+lua,或者知道set key value px milliseconds nx.后一种方式的核心实现命令如下: - 获取锁(unique ...
- 【Redis】分布式锁RedLock
普通实现 说道Redis分布式锁大部分人都会想到: 1.setnx+lua, 2.setkey value px milliseconds nx. - 获取锁(unique_value可以是UUID等 ...
- C# Redis分布式锁 - 单节点
为什么要用分布式锁? 先上一张截图,这是在浏览别人的博客时看到的. 在了解为什么要用分布式锁之前,我们应该知道到底什么是分布式锁. 锁按照不同的维度,有多种分类.比如 1.悲观锁,乐观锁; 2.公平锁 ...
- Redis分布式锁升级版RedLock及SpringBoot实现
分布式锁概览 在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问,Java中我们一般可以使用synchronized语法和ReetrantLock去保证,这实际上是本地锁的方式.但是现在 ...
- RedLock.Net - 基于Redis分布式锁的开源实现
工作中,经常会遇到分布式环境中资源访问冲突问题,比如商城的库存数量处理,或者某个事件的原子性操作,都需要确保某个时间段内只有一个线程在访问或处理资源. 因此现在网上也有很多的分布式锁的解决方案,有数据 ...
- Redis分布式锁
Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...
- Lua脚本在redis分布式锁场景的运用
目录 锁和分布式锁 锁是什么? 为什么需要锁? Java中的锁 分布式锁 redis 如何实现加锁 锁超时 retry redis 如何释放锁 不该释放的锁 通过Lua脚本实现锁释放 用redis做分 ...
随机推荐
- 阿里面试竟如此轻松,2招带你过关斩将拿下offer
在找工作之前首先是要认清一个问题,虽然这个问题比较俗,但是很现实,就是为什么追求高工资? 这个问题我想不用说大家心里也清楚.大部分人都不是当前城市的本地人,说好听了叫来上班,说的不好听其实叫“外来务工 ...
- python习题 随机密码生成 + 连续质数计算
随机密码生成 描述 补充编程模板中代码,完成如下功能: ...
- 【Spring注解驱动开发】AOP核心类解析,这是最全的一篇了!!
写在前面 昨天二狗子让我给他讲@EnableAspectJAutoProxy注解,讲到AnnotationAwareAspectJAutoProxyCreator类的源码时,二狗子消化不了了.这不,今 ...
- <string name="xxx"> 的复杂用法:格式化及使用html标签
1.官方文档: https://developer.android.com/guide/topics/resources/string-resource 2.格式化字符串 2.1 示例 <res ...
- 小程序mpvue中flyio的使用方法
Fly.js 一个基于Promise的.强大的.支持多种JavaScript运行时的http请求库. 有了它,您可以使用一份http请求代码在浏览器.微信小程序.Weex.Node.React Nat ...
- 剑指 Offer 43. 1~n整数中1出现的次数
题目描述 输入一个整数 n ,求1-n这n个整数的十进制表示中1出现的次数. 例如,输入12,1-12这些整数中包含1 的数字有1.10.11和12,1一共出现了5次. 示例 1: 输入:n = 12 ...
- java 将map转为实体类
使用反射将map转为对象,如果不使用反射的话需要一个get一个set写起来麻烦,并且不通用,所以写了一个通用的方法将map集合转为对象,直接看代码,注释也都挺清楚的 public static < ...
- 关于h5游戏开发,你想了解的一切都在这儿!
2020年,受疫情影响,线下产业红利褪去,线上迎来的新一轮的高峰.众多商家纷纷抓住了转型时机,开启了流量争夺战.H5游戏定制无疑是今年引流的大热门.如何开发一款有趣.有爆点.用户爱买单的好游戏呢? ...
- SpringBoot 消息国际化配置
一.目的 针对不同地区,设置不同的语言信息. SpringBoot国际化配置文件默认放在classpath:message.properties,如果自定义消息配置文件,需要application.p ...
- python3 变量
python 3变量名不能以数字开头但能数字结尾 变量名大小写敏感 在多个单词组成的变量名中以下划线间隔