在 .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实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...
随机推荐
- 元类、实现ORM
阅读目录: 元类 元类实现ORM 元类 1. 类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍然成立: >>> class Obj ...
- python ES连接服务器的方法
连接Elasticsearch(ES)服务器是进行数据搜索和分析的常用操作.Elasticsearch是一个基于Lucene的搜索引擎,提供了RESTful API来进行索引.搜索和管理数据. 以下是 ...
- C++ 简易消息循环
前言 本文将向大家介绍如何使用 C++ 的标准库实现一个异步和并发编程中都非常重要的编程模式:消息循环(Event Loop).尽管市面上存在不少库也提供了同样的功能,但有时候出于一些原因,我们并不想 ...
- Angular 19 "要" 来了⚡
前言 Angular 19 预计会在 11 月中旬发布,目前 (2024-10-27) 最新版本是 v19.0.0-next.11. 这次 v19 的改动可不小哦,新增了很多功能,甚至连 effect ...
- Linux Shell_read
介绍 从控制台输入 基本语法 read [选项] 参数 常用选项 |选项|功能| |-|-| |-p|指定读取值时的提示符| |-t|指定取值时等待的时间(秒),如果没有在指定时间输入,就不再等待了| ...
- 拯救php性能的神器webman-初入门
无意间发现的这个神器webman,真是秋名山上的腾源拓海! 该框架是workerman下的一个web开发的生态,我们可以先看看这里workerman的官方网站. workerman早有耳闻,知道它蛮厉 ...
- Redis中有事务吗?有何不同?
与关系型数据库事务的区别 Redis事务是指将多条命令加入队列,一次批量执行多条命令,每条命令会按顺序执行,事务执行过程中不会被其他客户端发来的命令所打断.也就是说,Redis事务就是一次性.顺序性. ...
- rust 终端输出 debug 信息
配置方法 将 env_logger log 添加到 Cargo.toml : 打开 Cargo.toml 文件并在 [dependencies] 部分下添加 env_logger log . [pac ...
- java中并发包简要分析01
参考<分布式java应用>一书,简单过一遍并发包(java.util.concurrent) ConcurrentHashMap ConcurrentHashMap是线程安全的HashMa ...
- qiankun 的 CSS 沙箱隔离机制
为什么需要CSS沙箱 在 qiankun 微前端框架中,由于每个子应用的开发和部署都是独立的,将主/子应用的资源整合到一起时,容易出现样式冲突的问题 因此,需要 CSS 沙箱来解决样式冲突问题,实现主 ...