.NET?Core使用Redis實(shí)現(xiàn)創(chuàng)建分布式鎖
在 .NET Core WebApi 中使用 Redis 創(chuàng)建分布式鎖可以通過 StackExchange.Redis 庫來實(shí)現(xiàn)。分布式鎖用于確保在分布式系統(tǒng)中,同一時(shí)間只有一個(gè)進(jìn)程可以執(zhí)行某段代碼。
1. 場景描述
在支付系統(tǒng)中,可能會(huì)出現(xiàn)以下并發(fā)問題:
- 用戶同時(shí)發(fā)起多次支付請求,導(dǎo)致重復(fù)扣款。
- 多個(gè)請求同時(shí)處理同一個(gè)訂單,導(dǎo)致數(shù)據(jù)不一致。
通過分布式鎖,可以確保同一時(shí)間只有一個(gè)請求能夠執(zhí)行關(guān)鍵操作(如扣款)。
2. 實(shí)現(xiàn)步驟
2.1 安裝 StackExchange.Redis 包
首先,安裝 Redis 客戶端庫:
dotnet add package StackExchange.Redis
2.2 配置 Redis 連接
在 appsettings.json 中添加 Redis 連接字符串:
{
"ConnectionStrings": {
"Redis": "localhost:6379"
}
}
2.3 創(chuàng)建分布式鎖工具類
創(chuàng)建一個(gè)工具類來封裝 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()
{
// 嘗試設(shè)置鎖,僅當(dāng)鍵不存在時(shí)才成功
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 服務(wù)
在 Startup.cs 或 Program.cs 中注冊 Redis 服務(wù):
// 添加 Redis 服務(wù)
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("Redis")));
2.4.2 創(chuàng)建支付控制器
在 Controllers 文件夾中創(chuàng)建一個(gè) 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)
{
// 創(chuàng)建分布式鎖
var lockKey = $"PaymentLock:{request.OrderId}"; // 鎖的鍵,基于訂單 ID
var lockValue = Guid.NewGuid().ToString(); // 鎖的值,確保唯一性
var expiry = TimeSpan.FromSeconds(10); // 鎖的過期時(shí)間
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 = "正在處理此訂單的另一個(gè)付款請求..." });
}
}
finally
{
// 釋放鎖
await distributedLock.ReleaseLockAsync();
}
}
}
3. 代碼說明
3.1 分布式鎖的實(shí)現(xiàn)
AcquireLockAsync: 使用 Redis 的 SET key value NX EX 命令嘗試獲取鎖。NX 表示僅在鍵不存在時(shí)設(shè)置,`EX 設(shè)置鍵的過期時(shí)間。
ReleaseLockAsync: 使用 Lua 腳本確保只有鎖的持有者才能釋放鎖,避免誤刪其他請求的鎖。
3.2 支付控制器的使用
鎖的鍵: 使用訂單 ID 作為鎖的鍵(如 PaymentLock:202501061410455506968463210),確保同一訂單的支付請求串行化。
鎖的值: 使用 GUID 作為鎖的值,確保鎖的唯一性。
鎖的過期時(shí)間: 設(shè)置合理的過期時(shí)間(如 10 秒),防止鎖被長時(shí)間占用。
3.3 支付處理邏輯
ProcessPaymentAsync: 模擬支付處理邏輯,包括調(diào)用支付網(wǎng)關(guān)、扣減余額等操作。
4. 測試 API
4.1 啟動(dòng) Web API
運(yùn)行項(xiàng)目,啟動(dòng) Web API。
4.2 發(fā)送支付請求
使用工具(如 Postman 或 curl)發(fā)送支付請求:
POST /api/payment/pay
Content-Type: application/json
{
"userId": "9527",
"orderId": "202501061410455506968463210"
}
4.3 測試并發(fā)場景
同時(shí)發(fā)送多個(gè)相同的支付請求,觀察是否只有一個(gè)請求能夠成功獲取鎖并處理支付。

5. 注意事項(xiàng)
鎖的粒度:
- 鎖的粒度要適中。如果鎖的粒度過大(如全局鎖),可能導(dǎo)致性能問題;如果粒度過小,可能增加復(fù)雜性。
- 在支付系統(tǒng)中,通常以訂單 ID 或用戶 ID 作為鎖的粒度。
鎖的過期時(shí)間:
- 設(shè)置合理的過期時(shí)間,避免鎖被長時(shí)間占用導(dǎo)致死鎖。
- 如果業(yè)務(wù)邏輯執(zhí)行時(shí)間較長,可以動(dòng)態(tài)延長鎖的過期時(shí)間。
鎖的可靠性:
Redis 需要高可用,否則可能導(dǎo)致鎖失效??梢允褂?Redis 集群或 Redlock 算法提高可靠性。
異常處理:
確保鎖的釋放操作放在 finally 塊中,避免因異常導(dǎo)致鎖無法釋放。
冪等性:
支付系統(tǒng)需要支持冪等性,即使多次請求,也只會(huì)產(chǎn)生一次扣款。
6. 總結(jié)
在 .NET Core Web API 中使用 Redis 創(chuàng)建分布式鎖,可以帶來以下好處:
- 解決并發(fā)問題,確保數(shù)據(jù)一致性。
- 提高系統(tǒng)的可靠性和性能。
- 簡化代碼邏輯,降低開發(fā)復(fù)雜度。
- 支持高并發(fā)、分布式環(huán)境和高可用需求。
通過合理使用 Redis 分布式鎖,可以構(gòu)建高可靠、高性能的分布式系統(tǒng),滿足復(fù)雜的業(yè)務(wù)需求。
到此這篇關(guān)于.NET Core使用Redis實(shí)現(xiàn)創(chuàng)建分布式鎖的文章就介紹到這了,更多相關(guān).NET Redis創(chuàng)建分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- .net core 3.1 Redis安裝和簡單使用
- 在.NET?Core中使用CSRedis的詳細(xì)過程
- ASP.NET?Core中使用Redis實(shí)現(xiàn)緩存
- .NET Core中使用Redis與Memcached的序列化問題詳析
- .net core如何使用Redis發(fā)布訂閱
- .net core使用redis基于StackExchange.Redis
- asp.net性能優(yōu)化之使用Redis緩存(入門)
- 詳解.NET中使用Redis數(shù)據(jù)庫
- 詳解Asp.net Core 使用Redis存儲(chǔ)Session
- 詳解如何在ASP.NET Core中使用Redis
- .net web優(yōu)雅地使用 redis的方法步驟
相關(guān)文章
asp.net c#采集需要登錄頁面的實(shí)現(xiàn)原理及代碼
當(dāng)我們采集頁面的時(shí)候,如果被采集的網(wǎng)站需要登錄才能采集,原理搞清楚了,就好辦了,我們所要做的僅僅是在采集的時(shí)候(或者說HttpWebRequest提交數(shù)據(jù)的時(shí)候),將Cookie信息放入Http請求頭里面就可以了,感興趣的朋友可以了解下,或許對你有所幫助2013-02-02
VS2012/VS2013本地發(fā)布網(wǎng)站問題集錦(HTTP錯(cuò)誤代碼)
這篇文章主要為大家詳細(xì)介紹了VS2012/VS2013本地發(fā)布網(wǎng)站遇到問題,HTTP錯(cuò)誤代碼的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
ASP.NET實(shí)現(xiàn)用戶注冊和驗(yàn)證功能(第4節(jié))
這篇文章主要介紹了ASP.NET實(shí)現(xiàn)用戶注冊和驗(yàn)證功能,學(xué)習(xí)ASP.NET驗(yàn)證控件的作用和使用方法,在此基礎(chǔ)上了解常用第三方控件,需要的朋友可以參考一下2015-08-08
asp.net用url重寫URLReWriter實(shí)現(xiàn)任意二級(jí)域名 高級(jí)篇
Asp.net 用url重寫(URLReWriter)實(shí)現(xiàn)任意二級(jí)域名,需要的朋友可以參考下,建議先看一下上一篇文章。2009-11-11
Asp.net內(nèi)置對象之Cookies(簡介/屬性方法/基本操作及實(shí)例)
本文將圍繞cookies了解Cookies對象/Cookie對象的屬性和方法/Cookie的基本操作及實(shí)例:Cookie的寫入和讀取/Cookie對象相比Session、Application的優(yōu)缺點(diǎn)扥等,感興趣的朋友可以了解下,或許對你學(xué)習(xí)cookies有所幫助2013-02-02

