在 .NET Core中如何使用 Redis 创建分布式锁
在 .NET Core WebApi 中使用 Redis 创建分布式锁可以通过 StackExchange.Redis 库来实现。分布式锁用于确保在分布式系统中,同一时间只有一个进程可以执行某段代码。
1. 场景描述
在支付系统中,可能会出现以下并发问题:
- 用户同时发起多次支付请求,导致重复扣款。
- 多个请求同时处理同一个订单,导致数据不一致。
通过分布式锁,可以确保同一时间只有一个请求能够执行关键操作(如扣款)。
2. 实现步骤
2.1 安装 StackExchange.Redis 包
首先,安装 Redis 客户端库:
dotnet add package StackExchange.Redis
2.2 配置 Redis 连接
在 appsettings.json 中添加 Redis 连接字符串:
{
"ConnectionStrings": {
"Redis": "localhost:6379"
}
}
2.3 创建分布式锁工具类
创建一个工具类来封装 Redis 分布式锁的逻辑:
using StackExchange.Redis;
using System;
using System.Threading.Tasks;
public class RedisDistributedLock
{
private readonly IDatabase _redisDatabase;
private readonly string _lockKey;
private readonly string _lockValue;
private readonly TimeSpan _expiry;
public RedisDistributedLock(IDatabase redisDatabase, string lockKey, string lockValue, TimeSpan expiry)
{
_redisDatabase = redisDatabase;
_lockKey = lockKey;
_lockValue = lockValue;
_expiry = expiry;
}
public async Task<bool> AcquireLockAsync()
{
// 尝试设置锁,仅当键不存在时才成功
return await _redisDatabase.StringSetAsync(_lockKey, _lockValue, _expiry, When.NotExists);
}
public async Task ReleaseLockAsync()
{
// 使用 Lua 脚本确保只有锁的持有者才能释放锁
var luaScript = @"
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
else
return 0
end";
await _redisDatabase.ScriptEvaluateAsync(luaScript, new RedisKey[] { _lockKey }, new RedisValue[] { _lockValue });
}
}
2.4 在 Web API 中使用分布式锁
在 Web API 的控制器中使用分布式锁来确保支付操作的原子性。
2.4.1 注册 Redis 服务
在 Startup.cs 或 Program.cs 中注册 Redis 服务:
// 添加 Redis 服务
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("Redis")));
2.4.2 创建支付控制器
在 Controllers 文件夹中创建一个 PaymentController,并在其中使用分布式锁:
using Microsoft.AspNetCore.Mvc;
using StackExchange.Redis;
using System;
using System.Threading.Tasks;
[ApiController]
[Route("api/[controller]")]
public class PaymentController : ControllerBase
{
private readonly IDatabase _redisDatabase;
public PaymentController(IConnectionMultiplexer redis)
{
_redisDatabase = redis.GetDatabase();
}
[HttpPost("pay")]
public async Task<IActionResult> ProcessPayment([FromBody] PaymentRequest request)
{
// 创建分布式锁
var lockKey = $"PaymentLock:{request.OrderId}"; // 锁的键,基于订单 ID
var lockValue = Guid.NewGuid().ToString(); // 锁的值,确保唯一性
var expiry = TimeSpan.FromSeconds(10); // 锁的过期时间
var distributedLock = new RedisDistributedLock(_redisDatabase, lockKey, lockValue, expiry);
try
{
// 尝试获取锁
if (await distributedLock.AcquireLockAsync())
{
Console.WriteLine("已获取锁,正在处理付款...");
// 模拟支付处理
bool paymentSuccess = await ProcessPaymentAsync(request.UserId, request.OrderId, request.Amount);
if (paymentSuccess)
{
return Ok(new { Message = "付款成功!" });
}
else
{
return BadRequest(new { Message = "付款失败!" });
}
}
else
{
return Conflict(new { Message = "正在处理此订单的另一个付款请求..." });
}
}
finally
{
// 释放锁
await distributedLock.ReleaseLockAsync();
}
}
}
3. 代码说明
3.1 分布式锁的实现
AcquireLockAsync: 使用Redis的SET key value NX EX命令尝试获取锁。NX表示仅在键不存在时设置,`EX 设置键的过期时间。ReleaseLockAsync: 使用Lua脚本确保只有锁的持有者才能释放锁,避免误删其他请求的锁。
3.2 支付控制器的使用
- 锁的键: 使用订单 ID 作为锁的键(如
PaymentLock:202501061410455506968463210),确保同一订单的支付请求串行化。 - 锁的值: 使用 GUID 作为锁的值,确保锁的唯一性。
- 锁的过期时间: 设置合理的过期时间(如 10 秒),防止锁被长时间占用。
3.3 支付处理逻辑
ProcessPaymentAsync: 模拟支付处理逻辑,包括调用支付网关、扣减余额等操作。
4. 测试 API
4.1 启动 Web API
运行项目,启动 Web API。
4.2 发送支付请求
使用工具(如 Postman 或 curl)发送支付请求:
POST /api/payment/pay
Content-Type: application/json
{
"userId": "9527",
"orderId": "202501061410455506968463210"
}
4.3 测试并发场景
同时发送多个相同的支付请求,观察是否只有一个请求能够成功获取锁并处理支付。

5. 注意事项
锁的粒度:
- 锁的粒度要适中。如果锁的粒度过大(如全局锁),可能导致性能问题;如果粒度过小,可能增加复杂性。
- 在支付系统中,通常以订单 ID 或用户 ID 作为锁的粒度。
锁的过期时间:
- 设置合理的过期时间,避免锁被长时间占用导致死锁。
- 如果业务逻辑执行时间较长,可以动态延长锁的过期时间。
锁的可靠性:
- Redis 需要高可用,否则可能导致锁失效。可以使用 Redis 集群或 Redlock 算法提高可靠性。
异常处理:
- 确保锁的释放操作放在
finally块中,避免因异常导致锁无法释放。
- 确保锁的释放操作放在
幂等性:
- 支付系统需要支持幂等性,即使多次请求,也只会产生一次扣款。
6. 总结
在 .NET Core Web API 中使用 Redis 创建分布式锁,可以带来以下好处:
- 解决并发问题,确保数据一致性。
- 提高系统的可靠性和性能。
- 简化代码逻辑,降低开发复杂度。
- 支持高并发、分布式环境和高可用需求。
通过合理使用 Redis 分布式锁,可以构建高可靠、高性能的分布式系统,满足复杂的业务需求。

在 .NET Core中如何使用 Redis 创建分布式锁的更多相关文章
- java中redis的分布式锁工具类
使用方式 try { if(PublicLock.getLock(lockKey)){ //这里写代码逻辑,执行完后需要释放锁 PublicLock.freeLock(lockKey); } } ca ...
- asp.net core mvc基于Redis实现分布式锁,C# WebApi接口防止高并发重复请求,分布式锁的接口幂等性实现
使用背景:在使用app或者pc网页时,可能由于网络原因,api接口可能被前端调用一个接口重复2次的情况,但是请求内容是一样的.这样在同一个短暂的时间内,就会有两个相同请求,而程序只希望处理第一个请求, ...
- Redis 中的原子操作(3)-使用Redis实现分布式锁
Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 R ...
- 从零到一手写基于Redis的分布式锁框架
1.分布式锁缘由 学习编程初期,我们做的诸如教务系统.成绩管理系统大多是单机架构,单机架构在处理并发的问题上一般是依赖于JDK内置的并发编程类库,如synchronize关键字.Lock类等.随着业务 ...
- 来吧,展示!Redis的分布式锁及其实现Redisson的全过程
前言 分布式锁是控制分布式系统之间同步访问共享资源的一种方式. 在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要 ...
- 用Redis构建分布式锁-RedLock(真分布)
在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增 ...
- 用Redis实现分布式锁 与 实现任务队列(转)
这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...
- 利用多写Redis实现分布式锁原理与实现分析(转)
利用多写Redis实现分布式锁原理与实现分析 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...
- Redis实现分布式锁
http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但 ...
- Redis实现分布式锁与任务队列
Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...
随机推荐
- SLAM中的各种地图
1.地图的不同分类方式 地图有多种不同的分类方式,网上有不少帖子介绍各种各样的地图,但并没有非常完整的总结地图应该怎么分类.论文[1]中将地图分成以下几种:拓扑地图.度量地图.度量-语义地图和混合地图 ...
- My SQL 列转行操作
原表结构如下,我们可以发现,"日运输量"和"车次"是在同一张表中相互独立的两个字段,即独立的两列数据,下面,我将系统中的测试数据以及代码全部放出来,以解释列转行 ...
- 鲜花:bitset求解高维偏序
书接上回 一维偏序直接做.二维偏序套线段树或归并排序.三维偏序可以树套树或者 CDQ 套树,那四维偏序呢?可以 CDQ 套树套树.那五维偏序呢?可以发现,无论是 CDQ 分治还是树,都很难再继续嵌套, ...
- 18.Kubernetes容器交付介绍
Kubernetes容器交付介绍 如何在k8s集群中部署Java项目 容器交付流程 开发代码阶段 编写代码 编写Dockerfile[打镜像做准备] 持续交付/集成 代码编译打包 制作镜像 上传镜像仓 ...
- 不同团队如何实现登录系统 (just for fun)
某一天 ceo 需要一个登录系统,找了开发团队 控制狂团队 领导点了卡布奇诺,打开了自己轻薄的 mac book, 点开 word 文档, 开始编写: 1. 项目背景 2. 名词解析 3. 数据表设计 ...
- pycharm生成的allure测试报告如何查看本地的index.html文件?
pycharm生成的allure测试报告应该是通过服务启动查看,但是如果把这个文件保存到本地查看,直接打开页面无内容 可以使用allure-combine工具实现本地正常打开 `from allure ...
- (Redis基础教程之八) 如何在Redis中管理Sets
如何在ubuntu18.04上安装和保护redis 如何连接到Redis数据库 如何管理Redis数据库和Keys 如何在Redis中管理副本和客户端 如何在Redis中管理字符串 如何在Redis中 ...
- 动态配置生成echarts图表
动态配置x轴和y轴的数据,并且可以选择柱状图.折线图.饼状图等图形 父组件代码: <template> <div class="reportPicture"> ...
- Linux之JSON处理工具jq
一个灵活的轻量级命令行JSON处理器 补充说明 jq 是 stedolan 开发的一个轻量级的和灵活的命令行JSON处理器,源码请参考 jq 项目主页 jq 用于处理JSON输入,将给定过滤器应用于其 ...
- golang之泛型
Go 1.18版本增加了对泛型的支持,泛型也是自 Go 语言开源以来所做的最大改变. 泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型.ーー换句话 ...