.NET Core Redis 分布式锁,在本文中,我们将讨论如何在 .NET Core 中使用 Redis 创建分布式锁。
我们在构建分布式系统的时候,会面临多个进程共同处理一个共享资源,由于一次只能有一个进程使用共享资源,会导致一些意想不到的问题!
我们可以使用分布式锁来解决这个问题。
为什么是分布式锁?
像往常一样,我们将使用锁来处理这个问题。
下面显示了一些演示锁的使用的示例代码。public void SomeMethod()
{
//do something...
lock(obj)
{
//do ....
}
//do something...
}
但是,这种锁并不能帮助我们解决问题!这是一个进程内锁,只能解决共享资源的一个进程。
这也是我们需要分布式锁的主要原因!
我将在这里使用 Redis 创建一个简单的分布式锁。
为什么我要使用 Redis 来完成这项工作?因为 Redis 的单线程特性和它执行原子操作的能力。
如何创建锁?
我将创建一个 .NET Core 控制台应用程序来向您展示。
在下一步之前,我们应该运行 Redis 服务器!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 对于应用程序的每个实例都必须是唯一的。而这个唯一值的作用就是解除锁定(unlock)。
最后我们还提供了一个持续时间(以毫秒为单位),超过这个时间后,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();
}
运行代码后,我们可能会得到如下结果。
只有一个人可以拿到锁!其他人在等待。
虽然锁会被Redis自动移除,但是也没有很好的利用好共享资源!
因为当一个进程完成它的工作时,它应该让其他人使用资源,而不是无休止地等待!
所以我们也需要释放锁。
如何释放锁?为了释放锁,我们只是删除 Redis 中的项目!正如我们在创建锁时所采取的,我们需要匹配资源的唯一值,这样释放正确的锁会更加安全。匹配时,我们将删除锁,即解锁成功。否则解锁不成功。而我们需要一次执行get和del命令,所以我们将使用一个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()}.");
}
});
运行示例后,您可能会得到以下结果。如您所见,第 3 个人和第 4 个人将在没有锁定的情况下继续运行。这是您可以在我的 github 页面中找到的源代码。
概括
.NET Core Redis 分布式锁,本文介绍了如何在 .NET Core 中使用 Redis 创建分布式锁。而且是基础版,您可以根据自己的业务进行改进。
注:用这个包
转载
https://www.muyuanzhan.com/tutorials/dotnet/9230.html
标签:Core,string,lock,Redis,person,NET,分布式 From: https://www.cnblogs.com/cxxtreasure/p/17179377.html