在 .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实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...
随机推荐
- Cartographer学习——2D栅格地图构建
前言: 到目前为止,对于点云数据的预处理过程已经介绍完毕,如:点云数据多传感器时间同步.运动畸变校正.重力校正.体素滤波等.做完这一系列的预备工作之后,实际上呢,就可以进行点云的扫描匹配了. 在讲解扫 ...
- 狂神说-Docker基础-学习笔记-03 日志、元数据、进程的查看
狂神说-Docker基础-学习笔记-03 日志.元数据.进程的查看 视频链接:https://www.bilibili.com/video/BV1og4y1q7M4?p=1 1.docker logs ...
- Google Guice 与 Noear Solon 的简单对照
1. 简介 Google Guice 是一个轻量级的依赖注入框架,它支持Java 5或者更高版本的JDK.Noear Solon 也是一个轻量级的依赖注入框架,它支持Java 8或者更高版本的JDK ...
- 开源 PHP 商城项目 CRMEB 安装和使用教程
说到电商系统,很多人第一反应可能是 Shopify 或 Magento.没错,这些平台确实功能强大,但是...它们也太强大了,不仅复杂还昂贵,对于刚起步的创业者来说简直是压力山大. 但是从零开始开发一 ...
- 一文彻底搞定Spring Security 认证,实现登陆登出功能
Spring Security 是一个强大且灵活的安全框架,提供了身份验证(认证)和授权(授权)功能.下面我们将详细介绍 Spring Security 的认证功能流程,并提供自定义实现登录接口的示例 ...
- 2023NOIP A层联测10 T4 子序列
2023NOIP A层联测10 T4 子序列 题面及数据范围 Ps:链接来自accoderOJ. 考场2小时才做完 T1,抱着试一试的心态看了 T4,然后想到做法了,调了 1 个多小时没调除了,赛后发 ...
- CF2027D The Endspeaker (Hard Version) 题解
题面 给你一个长度为 \(n\) 的数组 \(a\) 和一个长度为 \(m\) 的数组 \(b\) (所有 \(1 \le i < m\) 满足 \(b_i > b_{i+1}\) ).最 ...
- 高性能计算-gemm串行计算优化(3)
目标:Darknet 源码cpu矩阵乘法函数 gemm_nn 优化.参数说明:lda A的列数; ldb B的列数; ldc C的列数; M C的行数; K A的列数 测试方法:Darknet源码,m ...
- AbstractQueuedSynchronizer源码解析之ReentrantLock(二)
上篇文章分析了ReentrantLock的lock,tryLock,unlock方法,继续分析剩下的方法,首先开始lockInterruptibly,先看其API说明:lockInterruptibl ...
- Ubuntu默认启动到字符界面
修改/etc/default/grub sudo cp /etc/default/grub /etc/default/grub.bak sudo chmod 0777 /etc/default/gru ...