首页 > 数据库 >在 .NET Core中如何使用 Redis 创建分布式锁

在 .NET Core中如何使用 Redis 创建分布式锁

时间:2025-01-18 10:45:45浏览次数:1  
标签:Core redisDatabase Redis lockValue 支付 NET lockKey 分布式

在 .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 分布式锁,可以构建高可靠、高性能的分布式系统,满足复杂的业务需求。

标签:Core,redisDatabase,Redis,lockValue,支付,NET,lockKey,分布式
From: https://www.cnblogs.com/liyongqiang-cc/p/18655619

相关文章

  • Redis实训:社交关注关系存储任务
    一、实验目的1. 理解Redis的安装、配置及基本操作。2. 掌握Redis的不同数据类型及相应操作方法。3. 学习使用Java客户端连接Redis,并进行数据操作。4. 实践使用Redis存储社交关注关系的功能。二、实验环境准备1. JAVA环境准备:确保JavaDevelopmentKit(JDK)已......
  • Kubernetes (K8s) 权限管理指南
    1.引言Kubernetes(K8s)作为当今最流行的容器编排平台,其安全性至关重要。本指南旨在全面介绍K8s的权限管理机制,帮助具有一定基础的读者深入理解并掌握这一关键领域。©ivwdcwso(ID:u012172506)2.Kubernetes安全模型概述K8s的安全模型主要包括三个阶段:认证(......
  • Kubernetes(k8s)和Docker Compose本质区别
    Kubernetes(简称k8s)和DockerCompose是容器编排领域的两大重要工具,虽然它们都用于管理和编排容器化应用,但在设计目标、功能特性、使用场景和复杂度上存在显著差异。以下将从多个方面详细探讨Kubernetes和DockerCompose的本质区别。一、设计目标与应用场景1.KubernetesK......
  • cad.net CurveInfo类
    曲线信息类主要是缓存一层包围盒1,存档曲线Curve2,复合曲线CompositeCurve3d3,单元曲线Curve3d验证包围盒在这里:https://www.cnblogs.com/JJBox/p/18677417publicclassCurveInfo:DRect{publicintRegionColor=0;//染色:斜区0,横区1,竖区2publicMyGro......
  • 基于注意力机制和残差网络的轻量级图像分类模型(AR-LiteNet)
    新模型设计:基于注意力机制和残差网络的轻量级图像分类模型(AR-LiteNet)目录新模型设计:基于注意力机制和残差网络的轻量级图像分类模型(AR-LiteNet)引言1.AR-LiteNet简介2.AR-LiteNet的数学原理2.1卷积操作2.2通道注意力机制2.3残差连接2.4损失函数3.......
  • 基于 KubeSphere v4 的 Kubernetes 生产环境部署架构设计及成本分析
    本文作者:运维有术。今天分享的主题是:如何规划设计一个高可用、可扩展的中小规模生产级K8s集群?通过本文的指导,您将掌握以下设计生产级K8s集群的必备技能:集群规划能力合理规划节点规模和资源配置设计高可用的控制平面、计算平面、存储平面架构规划网络拓扑和安全策略制......
  • 【linux合集】redis集群部署
    集群式部署redis介绍:三台机器001、002、003然后再三台机器上面都部署redis_6379、redis_6380做主从,然后三台机器上面做集群1、安装/解压redismkdir-p/data/applications/wgethttps://download.redis.io/releases/redis-5.0.14.tar.gztar-xzf./redis-5.0.14.tar.gzcd......
  • Springboot(五十八)SpringBoot3使用Redisson实现接口的限流功能
    这部分我记录一下我使用redission实现接口限流的全过程。关于自定义注解,请移步《SpringBoot(二十六)SpringBoot自定义注解》一:redission自定义限流注解主要流程对接口实现限流,主要使用了Redisson提供的限流API方法;使用很简单:第一步:声明一个限流器; RRateLimiter rRateLim......
  • MiniMax TTS新模型T2A-01-HD:情感控制10秒克隆限时免费;真人表演+文本命令,Kinetix精准生
      开发者朋友们大家好: 这里是**「RTE开发者日报」**,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(Real-TimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编......
  • OpenAI 宕机思考丨Kubernetes 复杂度带来的服务发现系统的风险和应对措施
    作者:王建伟(正己)12月11日,OpenAI旗下AI聊天机器人平台ChatGPT、视频生成工具Sora及其面向开发人员的API自太平洋时间下午3点左右起发生严重中断,耗费约三个小时才顺利恢复所有服务。OpenAI在事后报告中写道,“该问题源自新部署的遥测服务,此项服务无意间压垮了Kuberne......