在 .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. Windows下如何用virtualenv创建虚拟环境

    虚拟环境可以有效的解决不同项目需要不同环境的问题,虚拟环境最大的好处就是可以将我们的开发环境进行隔离,让彼此之间不互相受影响.一.Windows下创建虚拟环境1.虚拟环境需要用到的库是virtuale ...

  2. downloadFile:base64数据导出文件,文件下载

    function downloadFile(filename, data){ let DownloadLink = document.createElement('a'); if ( Download ...

  3. flutter TabBarView 动态添加删除页面

    在TabBarView 动态添加页面后删除其中一个页面会导致后面的页面状态错误或删除的页面不正确.出现这种问题是由于创建子页面时没有为子页面设置唯一的key导致的. 1 void addNewPage ...

  4. 盘点Air780E的FTP应用,你了解吗?

    ​ 一.FTP 概述 FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一. FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端. ...

  5. 超实用!阿里云应用——Air780EP低功耗4G模组AT开发示例

    ​ Air780EP是合宙推出的一款低功耗4G全网通模组,兼容模组行业1618经典封装,支持OpenCPU开发及全功能数传AT开发,可广泛应用于多样化的物联网终端. 针对客户朋友需求反馈,本期特别推出 ...

  6. hyperf的使用

    hyperf是swoole的封装框架,用起来效率还是不错的. 使用方式看手册 https://hyperf.wiki/2.2/#/zh-cn/quick-start/install 其实是靠compo ...

  7. NetBox使用教程1-组织架构

    前言 本教程用于学习NetBox的基础使用,练习可使用官方Demo:https://demo.netbox.dev/ NetBox 使用教程系列:https://songxwn.com/tags/ne ...

  8. npm报错error:0308010C:digital envelope routines::unsupported

    error:0308010C:digital envelope routines::unsupported 出现这个错误是因为 node.js V17版本中最近发布的OpenSSL3.0, 而Open ...

  9. PHP编译安装之常见问题

    正式服的PHP环境,一般都会进行编译安装,汇总一下经常遇到的一些问题 1.Call to undefined function crmeb\utils\imagecreate 解决:需要安装gd库 1 ...

  10. Element-UI 中关于 Table 的几个功能点简介(行列的合并和样式、合计行配置等)

    〇.前言 本文记录了关于 Element 框架中 Table 的几个功能点,后续将持续更新. el-table 官网地址:https://element.eleme.cn/#/zh-CN/compon ...