在.Net中使用RedLock实现分布式锁

⒈简介

  RedLock 分布式锁算法由 Redis 的作者提出,大部分语言都有对应的实现,查看,RedLock.net 是 RedLock 分布式锁算法的 .NET 版实现,用来解决分布式下的并发问题。

  RedLock 的思想是使用多台 Redis Master ,节点之间完全独立,节点间不需要进行数据同步,因为 Master-Slave 架构一旦 Master 发生故障时数据没有复制到 Slave,被选为 Master 的 Slave 就丢掉了锁,另一个客户端就可以再次拿到锁。

  锁通过 setNX(原子操作) 命令设置,在有效时间内当获得锁的数量大于 (n/2+1) 代表成功,失败后需要向所有节点发送释放锁的消息。

  获取锁:

1 SET resource_name my_random_value NX PX 30000

  释放锁:

1 if redis.call("get",KEYS[1]) == ARGV[1] then
2     return redis.call("del",KEYS[1])
3 else
4     return 0
5 end

⒉使用

  1.创建 .NETCore API 项目

  2.Nuget 安装 RedLock.net

1 Install-Package RedLock.net

  3.appsettings.json 添加 redis 配置

 1 {
 2   "Logging": {
 3     "LogLevel": {
 4       "Default": "Warning"
 5     }
 6   },
 7   "AllowedHosts": "*",
 8   "RedisUrls": [
 9     "127.0.0.1:6379",
10     "192.168.214.128:6379"
11   ]
12 }

  4.添加 ProductService.cs,模拟商品购买

 1 // 有10个商品库存,如果同时启动多个API服务进行测试,这里改成存数据库或其他方式
 2 private static int stockCount = 10;
 3 public async Task<bool> BuyAsync()
 4 {
 5     // 模拟执行的逻辑代码花费的时间
 6     await Task.Delay(new Random().Next(100, 500));
 7     if (stockCount > 0)
 8     {
 9         stockCount--;
10         return true;
11     }
12     return false;
13 }

  5.修改 Startup.cs ,创建 RedLockFactory

    1.定义RedLockFactory属性

 1         private RedLockFactory lockFactory
 2         {
 3             get
 4             {
 5                 var redisUrls = Configuration.GetSection("RedisUrls").GetChildren().Select(s => s.Value).ToArray();
 6                 if(redisUrls.Length <= 0)
 7                 {
 8                     throw new ArgumentException("RedisUrl 不能为空");
 9                 }
10                 var endPoints = new List<RedLockEndPoint>();
11                 foreach (var item in redisUrls)
12                 {
13                     var arr = item.Split(":");
14                     endPoints.Add(new DnsEndPoint(arr[0], Convert.ToInt32(arr[1])));
15                 }
16                 return RedLockFactory.Create(endPoints);
17             }
18         }

    2.在 ConfigureServices 注入 IDistributedLockFactory:

1         // This method gets called by the runtime. Use this method to add services to the container.
2         public void ConfigureServices(IServiceCollection services)
3         {
4             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
5             services.AddSingleton(typeof(IDistributedLockFactory), lockFactory);
6             services.AddScoped(typeof(ProductService));
7         }

    3.修改 Configure,应用程序结束时释放 lockFactory

 1         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
 2         public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
 3         {
 4             if (env.IsDevelopment())
 5             {
 6                 app.UseDeveloperExceptionPage();
 7             }
 8 
 9             app.UseMvc();
10 
11             lifetime.ApplicationStopping.Register(() =>
12             {
13                 lockFactory.Dispose();
14             });
15 
16         }

  6.在 Controller 添加方法 DistributedLockTest

 1 private readonly IDistributedLockFactory _distributedLockFactory;
 2 private readonly ProductService _productService;
 3 
 4 public HomeController(IDistributedLockFactory distributedLockFactory,
 5     ProductService productService)
 6 {
 7     _distributedLockFactory = distributedLockFactory;
 8     _productService = productService;
 9 }
10 
11 [HttpGet]
12 public async Task<bool> DistributedLockTest()
13 {
14     var productId = "id";
15     // resource 锁定的对象
16     // expiryTime 锁定过期时间,锁区域内的逻辑执行如果超过过期时间,锁将被释放
17     // waitTime 等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间
18     // retryTime 等待时间内,多久尝试获取一次
19     using (var redLock = await _distributedLockFactory.CreateLockAsync(productId, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(20)))
20     {
21         if (redLock.IsAcquired)
22         {
23             var result = await _productService.BuyAsync();
24             return result;
25         }
26         else
27         {
28             Console.WriteLine($"获取锁失败:{DateTime.Now}");
29         }
30     }
31     return false;
32 }

 

  在文章RedLock 实现分布式锁基础之上修改部分代码编写。

 

  

  

posted @ 2019-05-29 11:26  SpringCore  阅读(4286)  评论(0编辑  收藏  举报