
在 .NET Core WebApi 中使用 Redis 创建分布式锁可以通过 StackExchange.Redis
库来实现。分布式锁用于确保在分布式系统中,同一时间只有一个进程可以执行某段代码。
在支付系统中,可能会出现以下并发问题:
通过分布式锁,可以确保同一时间只有一个请求能够执行关键操作(如扣款)。
首先,安装 Redis 客户端库:
dotnet add package StackExchange.Redis
在 appsettings.json
中添加 Redis 连接字符串:
{ "ConnectionStrings": { "Redis": "localhost:6379" } }
创建一个工具类来封装 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 }); } }
在 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(); } } }
AcquireLockAsync
: 使用 Redis
的 SET key value NX EX
命令尝试获取锁。NX
表示仅在键不存在时设置,`EX 设置键的过期时间。
ReleaseLockAsync
: 使用 Lua
脚本确保只有锁的持有者才能释放锁,避免误删其他请求的锁。
锁的键: 使用订单 ID 作为锁的键(如 PaymentLock:202501061410455506968463210
),确保同一订单的支付请求串行化。
锁的值: 使用 GUID 作为锁的值,确保锁的唯一性。
锁的过期时间: 设置合理的过期时间(如 10 秒),防止锁被长时间占用。
ProcessPaymentAsync
: 模拟支付处理逻辑,包括调用支付网关、扣减余额等操作。
运行项目,启动 Web API。
使用工具(如 Postman 或 curl)发送支付请求:
POST /api/payment/pay Content-Type: application/json { "userId": "9527", "orderId": "202501061410455506968463210" }
同时发送多个相同的支付请求,观察是否只有一个请求能够成功获取锁并处理支付。
锁的粒度:
锁的过期时间:
锁的可靠性:
Redis 需要高可用,否则可能导致锁失效。可以使用 Redis 集群或 Redlock 算法提高可靠性。
异常处理:
确保锁的释放操作放在 finally
块中,避免因异常导致锁无法释放。
幂等性:
支付系统需要支持幂等性,即使多次请求,也只会产生一次扣款。
在 .NET Core Web API 中使用 Redis 创建分布式锁,可以带来以下好处:
通过合理使用 Redis 分布式锁,可以构建高可靠、高性能的分布式系统,满足复杂的业务需求。
到此这篇关于.NET Core使用Redis实现创建分布式锁的文章就介绍到这了,更多相关.NET Redis创建分布式锁内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!