在 .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.csProgram.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: 使用 RedisSET 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. 注意事项

  1. 锁的粒度:

    • 锁的粒度要适中。如果锁的粒度过大(如全局锁),可能导致性能问题;如果粒度过小,可能增加复杂性。
    • 在支付系统中,通常以订单 ID 或用户 ID 作为锁的粒度。
  2. 锁的过期时间:

    • 设置合理的过期时间,避免锁被长时间占用导致死锁。
    • 如果业务逻辑执行时间较长,可以动态延长锁的过期时间。
  3. 锁的可靠性:

    • Redis 需要高可用,否则可能导致锁失效。可以使用 Redis 集群或 Redlock 算法提高可靠性。
  4. 异常处理:

    • 确保锁的释放操作放在 finally 块中,避免因异常导致锁无法释放。
  5. 幂等性:

    • 支付系统需要支持幂等性,即使多次请求,也只会产生一次扣款。

6. 总结

在 .NET Core Web API 中使用 Redis 创建分布式锁,可以带来以下好处:

  • 解决并发问题,确保数据一致性。
  • 提高系统的可靠性和性能。
  • 简化代码逻辑,降低开发复杂度。
  • 支持高并发、分布式环境和高可用需求。

通过合理使用 Redis 分布式锁,可以构建高可靠、高性能的分布式系统,满足复杂的业务需求。

在 .NET Core中如何使用 Redis 创建分布式锁的更多相关文章

  1. java中redis的分布式锁工具类

    使用方式 try { if(PublicLock.getLock(lockKey)){ //这里写代码逻辑,执行完后需要释放锁 PublicLock.freeLock(lockKey); } } ca ...

  2. asp.net core mvc基于Redis实现分布式锁,C# WebApi接口防止高并发重复请求,分布式锁的接口幂等性实现

    使用背景:在使用app或者pc网页时,可能由于网络原因,api接口可能被前端调用一个接口重复2次的情况,但是请求内容是一样的.这样在同一个短暂的时间内,就会有两个相同请求,而程序只希望处理第一个请求, ...

  3. Redis 中的原子操作(3)-使用Redis实现分布式锁

    Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 R ...

  4. 从零到一手写基于Redis的分布式锁框架

    1.分布式锁缘由 学习编程初期,我们做的诸如教务系统.成绩管理系统大多是单机架构,单机架构在处理并发的问题上一般是依赖于JDK内置的并发编程类库,如synchronize关键字.Lock类等.随着业务 ...

  5. 来吧,展示!Redis的分布式锁及其实现Redisson的全过程

    前言 分布式锁是控制分布式系统之间同步访问共享资源的一种方式. 在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要 ...

  6. 用Redis构建分布式锁-RedLock(真分布)

    在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增 ...

  7. 用Redis实现分布式锁 与 实现任务队列(转)

    这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说分享思路比分享代码更重要(貌似大概是这个意 ...

  8. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  9. Redis实现分布式锁

    http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但 ...

  10. Redis实现分布式锁与任务队列

    Redis实现分布式锁 与 实现任务队列 这一次总结和分享用Redis实现分布式锁 与 实现任务队列 这两大强大的功能.先扯点个人观点,之前我看了一篇博文说博客园的文章大部分都是分享代码,博文里强调说 ...

随机推荐

  1. Linux基础-查看和设置环境变量

    一,查看环境变量 二,环境变量类型 三,设置环境变量 四,参考资料 一,查看环境变量 在 Linux中,环境变量是一个很重要的概念.环境变量可以由系统.用户.Shell 以及其他程序来设定,其是保存在 ...

  2. Next.js 从零入门到实战 3:2024最新完整教程 | 包含SSR、API路由和性能优化

    CSS样式学习 上一篇文章讲到如何创建一个脚手架程序,目前我们已经有了一个nextjs的基本框架,因此我们可以在这个基础上进行改造.打开我们项目中的page.tsx页面,这里是程序的主页面,也就是我们 ...

  3. 访问浪潮带外BMC界面的远程控制台重定向(KVM)无法访问,提示JViewer未签名,mac电脑安装JDK8

    报错截图: 安装JDK8 下载JDK1.8的安装包 Java Downloads | Oracle 下载的安装包双击按提示流程安装: 按照完成以后.我们可以查看JDK的安装路径.在资源库/Librar ...

  4. Nuxt.js 应用中的 vite:extendConfig 事件钩子详解

    title: Nuxt.js 应用中的 vite:extendConfig 事件钩子详解 date: 2024/11/12 updated: 2024/11/12 author: cmdragon e ...

  5. Go获取文件路径,文件名,后缀

    import ( "fmt" "os" "path/filepath" "path" ) files := " ...

  6. 【深入Java虚拟机】之七:Java编译与JIT编译

    编译过程 不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤进行: 其中绿色的模块可以选择性实现.很容易看出,上图中 ...

  7. PySAGES实记

    技术背景 PySAGES是一款可以使用GPU加速的增强采样插件,它可以直接对接到OpenMM上进行增强采样分子动力学模拟,这里我们测试一下相关的安装,并尝试跑一个简单的增强采样示例. 安装PySAGE ...

  8. 分布式事务之dtm

    github: https://github.com/dtm-labs/dtm 本人使用场景, 目前微服务中存在的用户服务, 商品服务,订单服务, 支付服务, 在进行下单操作的时候,需要创建订单并扣减 ...

  9. 使用阿里的ARTHAS跟踪方法耗时

    使用命令跟踪一个方法的耗时 在arthas 命令行下输入命令 trace 类全路径 监控的方法 trace com.redxun.bpm.core.service.BpmInstServiceImpl ...

  10. .NET Core 基于 IHostedService 实现后台定时任务

    .NET Core 基于 IHostedService 实现后台定时任务 迷恋自留地 NET Core 2.0 引入了 IHostedService ,基于它可以很方便地执行后台任务,.NET Cor ...