获取锁:
SET resource_name my_random_value NX PX 30000
释放锁:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
RedLock.net 集成:
- 创建 .NETCore API 项目
- NuGet 安装 RedLock.net
Install-Package RedLock.net
- appsettings.json 添加 redis 配置
{
"RedisUrl": "127.0.0.1:6379", // 多个用,分割
...
}
- 添加 ProductService.cs,模拟商品购买
// 有10个商品库存,如果同时启动多个API服务进行测试,这里改成存数据库或其他方式
private static int stockCount = 10;
public async Task<bool> BuyAsync()
{
// 模拟执行的逻辑代码花费的时间
await Task.Delay(new Random().Next(100, 500));
if (stockCount > 0)
{
stockCount--;
return true;
}
return false;
}
- 修改 Startup.cs ,创建 RedLockFactory
定义 RedLockFactory 变量:
private RedLockFactory lockFactory;
添加方法:
private RedLockFactory GetRedLockFactory()
{
var redisUrl = Configuration["RedisUrl"];
if (string.IsNullOrEmpty(redisUrl))
{
throw new ArgumentException("RedisUrl 不能为空");
}
var urls = redisUrl.Split(",").ToList();
var endPoints = new List<RedLockEndPoint>();
foreach (var item in urls)
{
var arr = item.Split(":");
endPoints.Add(new DnsEndPoint(arr[0], Convert.ToInt32(arr[1])));
}
return RedLockFactory.Create(endPoints);
}
在 ConfigureServices 注入 IDistributedLockFactory:
lockFactory = GetRedLockFactory();
services.AddSingleton(typeof(IDistributedLockFactory), lockFactory);
services.AddScoped(typeof(ProductService));
修改 Configure,应用程序结束时释放 lockFactory :
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
{
...
lifetime.ApplicationStopping.Register(() =>
{
lockFactory.Dispose();
});
}
- 在 Controller 添加方法 DistributedLockTest
private readonly IDistributedLockFactory _distributedLockFactory;
private readonly ProductService _productService;
public HomeController(IDistributedLockFactory distributedLockFactory,
ProductService productService)
{
_distributedLockFactory = distributedLockFactory;
_productService = productService;
}
[HttpGet]
public async Task<bool> DistributedLockTest()
{
var productId = "id";
// resource 锁定的对象
// expiryTime 锁定过期时间,锁区域内的逻辑执行如果超过过期时间,锁将被释放
// waitTime 等待时间,相同的 resource 如果当前的锁被其他线程占用,最多等待时间
// retryTime 等待时间内,多久尝试获取一次
using (var redLock = await _distributedLockFactory.CreateLockAsync(productId, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(20)))
{
if (redLock.IsAcquired)
{
var result = await _productService.BuyAsync();
return result;
}
else
{
Console.WriteLine($"获取锁失败:{DateTime.Now}");
}
}
return false;
}
- 调用接口测试
Parallel.For(1, 16, (i) =>
{
var stopwatch = new Stopwatch();
stopwatch.Start();
var data = GetAsync($"http://localhost:5000/home/distributedLockTest").Result;
stopwatch.Stop();
Console.WriteLine($"ThreadId:{Thread.CurrentThread.ManagedThreadId}, Result:{data}, Time:{stopwatch.ElapsedMilliseconds}");
});