首页 > 数据库 >在 .NET Core 中使用 Redis 创建分布式锁

在 .NET Core 中使用 Redis 创建分布式锁

时间:2023-03-04 22:33:35浏览次数:46  
标签:Core string lock Redis person NET 分布式

.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

相关文章

  • Ethernet II
    数据链路层,以太帧主要有4种格式:EthernetII以太帧EthernetII帧,也称为Ethernetv2帧,是如今局域网里最常见的以太帧,是以太网事实标准。如今大多数的TCP/IP应用(如HTTP、......
  • lua脚本在redis中的使用
    先开启redis的日志输出修改redis.conf文件,设置logfile/root/tools/redis-6.0.9/logs/redis.log重启redissystemctlrestartredisd创建一个简单的lua脚本test.......
  • Net常用类记录
    Encoder类将一组字符转换为一个字节序列Dns类提供简单的域名解析功能。C#获取本机的串口号C#获取本机的串口号  usingSystem.IO.Ports;  //头文件  s......
  • docker启动创建容器时,报错Cannot link to /mysql, as it does not belong to the defa
    启动创建容器时,报错Cannotlinkto/mysql,asitdoesnotbelongtothedefaultnetwork从报错信息看是不属于默认网络分析容器网络通过dockerinspect容器id先......
  • ASP.NET Core 中间件
    中间件是一种装配到应用管道以处理请求和响应的软件。每个组件:选择是否将请求传递到管道中的下一个组件。可在管道中的下一个组件前后执行工作。使用 WebApplicati......
  • 【Redis场景5】集群秒杀优化-分布式锁
    集群环境下的秒杀问题前序【Redis场景1】用户登录注册【Redis场景2】缓存更新策略(双写一致)【Redis场景3】缓存穿透、击穿问题【Redis场景拓展】秒杀问题-全局唯一ID......
  • 从零带你写netty
    提到netty,我们总会说它是Java语言中网络编程的首选框架?为什么?因为它性能太高了。这也是很多人热衷学习它的原因。也许点进这篇文章的你,就怀着这样的目的。但是,请你认真审......
  • 《VERY DEEP CONVOLUTIONAL NETWORKS FOR LARGE-SCALE IMAGE RECOGNITION (VGG) 2014
    abstract在这项工作中,作者研究了卷积网络深度对其在大规模图像识别设置中的准确性的影响。其主要贡献是:使用非常小(3×3)卷积滤波器的架构,对更深的网络进行了全面评估,通......
  • C#初步学习3(个人笔记,基于老赵.Net的视频自学,不喜勿喷)
    //此笔记仅针对个人学习而写,会有所缺失的内容,不喜勿喷初步学习C#中的方法C#中的方法和java中的也十分相似,但C#中的方法是默认被“private”修饰的如下图所示同样的,若......
  • .NET静态代码织入——肉夹馍(Rougamo) 发布1.4.0
    肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应用启动的初始化时......