使用Redis创建分布式锁

点击上方蓝色字关注我们~


在本文中,我们将讨论如何在.NET Core中使用Redis创建分布式锁。


当我们构建分布式系统时,我们将面临多个进程一起处理共享资源,由于其中只有一个可以一次使用共享资源,因此会导致一些意外问题!


我们可以使用分布式锁来解决这个问题。


为什么分布式锁?

首先在非集群单体应用下,我们使用锁来处理这个问题。


以下显示了一些演示锁的使用的示例代码。

public void SomeMethod()	
{  	
//do something...  	lock(obj)  	{  	
//do ....  	}  	
//do something...  	
}  



但是,这种类型的锁不能帮助我们很好地解决问题!这是一个进程内锁,只能用共享资源解决一个进程。 

这也是我们需要分布式锁的主要原因! 

我将使用Redis在这里创建一个简单的分布式锁。


为什么我使用Redis来完成这项工作?由于Redis的单线程特性及其执行原子操作的能力。


如何创建一个锁?

我将创建一个.NET Core Console应用程序来向您展示大概流程。


在下一步之前,我们应该运行Redis服务器!

640?wx_fmt=png

StackExchange.Redis是.NET中最受欢迎的Reids客户端,我们将使用它来完成以下工作。


首先与Redis建立联系。

/// <summary>  	
/// The lazy connection.  	
/// </summary>  	
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>  	
{  	ConfigurationOptions configuration = new ConfigurationOptions  	{  	AbortOnConnectFail = false,  	ConnectTimeout = 5000,  	};  	configuration.EndPoints.Add("localhost", 6379);  	return ConnectionMultiplexer.Connect(configuration.ToString());  	
});  	/// <summary>  	
/// Gets the connection.  	
/// </summary>  	
/// <value>The connection.</value>  	
public static ConnectionMultiplexer Connection => lazyConnection.Value;

为了请求锁定共享资源,我们执行以下操作:

SET resource_name unique_value NX PX duration 

resource_name是应用程序的所有实例将共享的值。


unique_value必须对应用程序的每个实例都是唯一的。而他的主要目的是取消锁定(解锁)。


最后,我们还提供一个持续时间(以毫秒为单位),之后Redis将自动删除锁定。


这是C#代码中的实现。

/// <summary>  	
/// Acquires the lock.  	
/// </summary>  	
/// <returns><c>true</c>, if lock was acquired, <c>false</c> otherwise.</returns>  	
/// <param name="key">Key.</param>  	
/// <param name="value">Value.</param>  	
/// <param name="expiration">Expiration.</param>  	
static bool AcquireLock(string key, string value, TimeSpan expiration)	
{  	bool flag = false;  	try	{  	flag = Connection.GetDatabase().StringSet(key, value, expiration, When.NotExists);  	}  	catch (Exception ex)  	{  	Console.WriteLine($"Acquire lock fail...{ex.Message}");  	flag = true;  	}  	return flag;  	
}

这是测试获取锁定的代码。

static void Main(string[] args)	
{  	string lockKey = "lock:eat";  	TimeSpan expiration = TimeSpan.FromSeconds(5);  	
//5 person eat something...  	Parallel.For(0, 5, x =>  	{  	string person = $"person:{x}";  	bool isLocked = AcquireLock(lockKey, person, expiration);  	if (isLocked)  	{  	Console.WriteLine($"{person} begin eat food(with lock) at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}.");  	}  	
else	{  	Console.WriteLine($"{person} can not eat food due to don't get the lock.");  	}  	});  	Console.WriteLine("end");  	Console.Read();  	
}  

 运行代码后,我们可能会得到以下结果。

640?wx_fmt=png

只有一个人可以获得锁定!其他人等待。


虽然Redis会自动删除锁,但它也没有很好地利用共享资源!


因为当一个进程完成它的工作时,应该让其他人使用该资源,而不是无休止地等待!


所以我们也需要释放锁。

如何释放锁定?

要释放锁,我们只需删除Redis中对应的key/value!


正如我们在创建锁中所做的那样,我们需要匹配资源的唯一值,这样可以更安全地释放正确的锁。


匹配时,我们将删除锁定,这意味着解锁成功。否则,解锁不成功。


我们需要一次执行getdel命令,因此我们将使用lua脚本来执行此操作!

/// <summary>  	
/// Releases the lock.  	
/// </summary>  	
/// <returns><c>true</c>, if lock was released, <c>false</c> otherwise.</returns>  	
/// <param name="key">Key.</param>  	
/// <param name="value">Value.</param>  	
static bool ReleaseLock(string key, string value)	
{  	string lua_script = @"  	if (redis.call('GET', KEYS[1]) == ARGV[1]) then  	redis.call('DEL', KEYS[1])  	return true  	else  	return false  	end  	";  	try	{  	var res = Connection.GetDatabase().ScriptEvaluate(lua_script,  	new RedisKey[] { key },  	new RedisValue[] { value });  	return (bool)res;  	}  	catch (Exception ex)  	{  	Console.WriteLine($"ReleaseLock lock fail...{ex.Message}");  	return false;  	}  	
}  

我们应该在进程完成后调用此方法。


当进程获得锁定并且由于某些原因而未释放锁定时,其他进程不能等到它被释放。此时,其他流程应该继续进行。


这是一个处理这个场景的示例。

Parallel.For(0, 5, x =>  	
{  	string person = $"person:{x}";  	var val = 0;  	bool isLocked = AcquireLock(lockKey, person, expiration);  	while (!isLocked && val <= 5000)  	{  	val += 250;  	System.Threading.Thread.Sleep(250);  	isLocked = AcquireLock(lockKey, person, expiration);  	}  	if (isLocked)  	{  	Console.WriteLine($"{person} begin eat food(with lock) at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}.");  	if (new Random().NextDouble() < 0.6)  	{  	Console.WriteLine($"{person} release lock {ReleaseLock(lockKey, person)}  {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");  	}  	
else	{  	Console.WriteLine($"{person} do not release lock ....");  	}  	}  	
else	{  	Console.WriteLine($"{person} begin eat food(without lock) at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}.");  	}  	
});  

运行该示例后,您将会得到以下结果。

640?wx_fmt=png


如图所示,第3和第4在无锁情况下运行。

640?wx_fmt=jpeg




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/314697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NET Core Web Api之JWT VS Session VS Cookie(二)

本文我们来探讨下JWT VS Session的问题&#xff0c;我们可直接抛出问题&#xff1a;使用客户端存储的JWT比服务端维持Session更好吗&#xff1f; 既然要比较JWT VS Session&#xff0c;那我们就得知道为何需要JWT和Session&#xff0c;它们共同是为了解决什么问题呢&#xff1f…

P4721 【模板】分治 FFT

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 写一下式子&#xff0c;发现每个fif_ifi​只有左边的fff对他有影响&#xff0c;所以考虑分治FFTFFTFFT来解决这个问题。 先递归左边&#xff0c;让后计算对右边贡献&#xff0c;再递归右边…

程序员生活之路--来自程序员爸爸的一封信

亲爱的孩子&#xff1a;当你看到爸爸这封信的时候&#xff0c;说明你已经长大了或者已经会玩微信公众号了&#xff0c;当然爸爸还是希望你长大了&#xff0c;并不希望你那么小就天天抱着手机刷微信。我写这个文章的时候正直盛夏&#xff0c;现在外边正是37度的高温&#xff0c;…

C#上位机与欧姆龙PLC的通信05---- HostLink协议

1、介绍 Hostlink协议是欧姆龙PLC与上位机链接的公开协议。上位机通过发送Hostlink命令&#xff0c;可以对PLC进行I/O读写、可以对PLC进行I/O读写、改变操作模式、强制置位/复位等操作。由于是公开协议&#xff0c;即便是非欧姆龙的上位设备&#xff08;软件&#xff09;&…

Codeforces Round #737 (Div. 2) D. Ezzat and Grid 线段树动态开点

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 比较套路的一个题&#xff0c;我们维护一个dp[i]dp[i]dp[i]表示到了第iii行能保留的区间最多是多少。 转移比较明显&#xff1a;dp[i]max(dp[j])dp[i]max(dp[j])dp[i]max(dp[j]) 其中jjj能…

从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件

标题&#xff1a;从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者&#xff1a;Lamond Lu地址&#xff1a;https://www.cnblogs.com/lwqlun/p/11260750.html源代码&#xff1a;https://github.com/lamondlu/DynamicPlugins前情回顾•从零开始实现ASP…

SP1043 GSS1 - Can you answer these queries I 猫树

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 猫树是一种可以O(nlogn)O(nlogn)O(nlogn)预处理&#xff0c;O(1)O(1)O(1)查询的数据结构。预处理的信息应该满足可合并的性质&#xff0c;与线段树pushuppushuppushup的原理相同&#xff0…

动手造轮子:基于 Redis 实现 EventBus

动手造轮子&#xff1a;基于 Redis 实现 EventBusIntro上次我们造了一个简单的基于内存的 EventBus&#xff0c;但是如果要跨系统的话就不合适了&#xff0c;所以有了这篇基于 Redis 的 EventBus 探索。本文的实现是基于 StackExchange.Redis 来实现。RedisEventStore 实现既然…

最小生成树KrusKal算法(并查集)

洛谷p1111链接 克鲁斯卡尔算法的思路就是由森林变成树的过程&#xff0c;其中最主要的就是贪心和并查集的应用。 我们知道链接n个点需要n-1条边&#xff0c;这就满足的最后生成的是一颗树&#xff0c;而不是一个环。在这n-1条边的选择上我们又要尽可能的让边的权重小&#xff0…

#6278. 数列分块 2 分块 + 块内二分

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 真 调一晚上血压上来了。 考虑第一个操作&#xff0c;块内打个标记&#xff0c;其他的暴力查询即可。 考虑第二个操作&#xff0c;讲块内元素排序之后&#xff0c;直接二分查询。 注意修改…

使用腾讯云提供的针对Nuget包管理器的缓存加速服务

继阿里巴巴开源镜像站&#xff08;https://opsx.alibaba.com/&#xff09;、华为云镜像站点&#xff08;https://mirrors.huaweicloud.com/ &#xff09;之后&#xff0c;腾讯也已于近日上线了类似的服务&#xff0c;官方名称为腾讯云软件源&#xff08;Tencent Open Source Mi…

最小生成树Prime算法

洛谷p1546链接 Prime算法的核心也是贪心&#xff0c;但是不同的就是&#xff0c;它是一直维护一颗树&#xff0c; 直到变成一颗最小生成树&#xff0c; #include<bits/stdc.h> using namespace std; const int maxn 110; const int inf 0x3f3f3f3f; int maze[maxn][m…

#6284. 数列分块 8 分块

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 乍一看貌似没有什么东西能维护块内同一个数的个数&#xff0c;但是通过第六感可以发现每次操作后区间都会被推成一个数&#xff0c;那么我们分个块&#xff0c;让后块内打个标记&#xff0…

最短路弗洛伊德(Floyd)算法加保存路径

弗洛伊德算法大致有点像dp的推导 dp[i][j] min(dp[i][k] dp[k][j], dp[i][j]), 其中 i 是起始点&#xff0c;j 是终止点。k是它们经过的中途点。 通过这个公式不断地更新dp[i][j],得到最短路径长。 我们先定义两个矩阵&#xff0c;minpath[i][j],表示的是从 i 到 j 当前得到的…

云考古 | Azure 自建 RDS 让 iPad 跑 Office 97

导语苹果一直在尝试把iPad做成电脑&#xff0c;但效果始终不如真正的PC理想。如果能在iPad上运行PC软件&#xff0c;如完整版的Office&#xff0c;那一定是一种非常理想的方式。我小时候电脑启蒙使用的第一个软件就是Office 97里的Word&#xff0c;这也是第一款引入Office助手&…

P3338 [ZJOI2014]力 FFT + 推式子

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 这个式子看起来很FFTFFTFFT&#xff0c;让我们来化简一下。 考虑EEE中直接将qiq_iqi​约掉&#xff0c;所以Ei∑j1i−1qj(i−j)2−∑ji1nqj(i−j)2E_i\sum_{j1}^{i-1}\frac{q_j}{(i-j)^2}-…

DevOps案例研究:庖丁解牛,剖析Google持续交付之道

内容来源&#xff1a;DevOps案例深度研究 –Google持续交付实践战队&#xff08;本文只展示部分PPT及研究成果&#xff0c;更多细节请关注案例分享会&#xff0c;及本公众号。&#xff09;本案例内容贡献者&#xff1a;姚元庆 (Topic Leader) 、任跃兵、王红阳、王晓敏、张彪本…

架构杂谈《八》Docker 架构

Docker 架构 一、Docker 引擎的三大组件1&#xff09;Docker 后台服务&#xff08;Docker Daemon&#xff09;&#xff1a;是长时间运行在后台的守护进程&#xff0c;是Docker的核心服务&#xff0c;可以通过命令dockerd与它进行交互通信。2&#xff09;REST 接口&#xff08;R…

P3723 [AH2017/HNOI2017]礼物 FFT + 式子化简

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 首先可以知道&#xff0c;我们对某个数组加上一个正数数的操作可以转换成对一个数组加上一个任意数&#xff0c;所以我们设变化量为xxx。 对于∑i1n(ai−bi)2\sum_{i1}^n(a_i-b_i)^2i1∑n​…

.net core 基于 IHostedService 实现定时任务

.net core 基于 IHostedService 实现定时任务Intro从 .net core 2.0 开始&#xff0c;开始引入 IHostedService&#xff0c;可以通过 IHostedService 来实现后台任务&#xff0c;但是只能在 WebHost 的基础上使用。从 .net core 2.1 开始微软引入通用主机( GenericHost)&#x…