MASA Framework的分散式鎖設計
前言
什麼是鎖?什麼是分散式鎖?它們之間有什麼樣的關係?
什麼是鎖
加鎖(lock)是2018年公佈的計算機科學技術名詞,是指將控制變數置位,控制共享資源不能被其他執行緒訪問。通過加鎖,可以確保在同一時刻只有一個執行緒在訪問被鎖住的程式碼片段,我們在單機部署時可使用最簡單的加鎖完成資源的獨享,如:
public class Program
{
private static readonly object Obj = new { };
public static void Main()
{
lock (obj)
{
//同一時刻只有一個執行緒可以訪問
}
}
}
什麼是分散式鎖
但隨著業務發展的需要,原單體單機部署的系統被部署成分散式集群系統後,原來的併發控制策略失效,為了解決這個問題就需要引入分散式鎖,那分散式鎖應該具備哪些條件?
- 原子性:在分散式環境下,一個方法在同一個時間點只能被一臺機器下的一個執行緒所執行,防止資料資源的併發訪問,避免資料不一致情況
- 高可用:具備自動失效機制,防止死鎖,獲取鎖後如果出現錯誤,並且無法釋放鎖,則使用租約一段時間後自動釋放鎖
- 阻塞性:具備非阻塞鎖特性(沒有獲取到鎖時直接返回獲取鎖失敗,不會長時間因等待鎖導致阻塞)
- 高效能:高效能的獲取鎖與釋放鎖
- 可重入性:具備可重入特性,在同一執行緒外層函式獲得鎖之後,內層方法會自動獲取鎖
實現
分散式鎖是特定於實現的,目前MasaFramework提供了兩個實現,分別是Local
、Medallion
,下面會介紹如何配置並使用它們
本地鎖
是基於SemaphoreSlim
實現的,它不是真正的分散式鎖,我們建議你在開發和測試環境中使用它,不需要聯網也不會與其他人衝突
Medallion
是基於DistributedLock實現的分散式鎖,它提供了很多種技術的實現,包括Microsoft SQL Server
、Postgresql
、MySQL 或 MariaDB
、Oracle
、Redis
、Azure blob
、Apache ZooKeeper
、鎖檔案
、作業系統全域性WaitHandles(Windows)
,我們只需要任選一種實現即可,目前Medallion
提供的分散式鎖並不支援可重入性,點選瞭解原因
快速入門
- 安裝.NET 6.0
以本地鎖單應用鎖為例:
- 新建ASP.NET Core 空專案
Assignment.DistributedLock.Local
,並安裝Masa.Contrib.Data.DistributedLock.Local
dotnet new web -o Assignment.DistributedLock.Local
cd Assignment.DistributedLock.Local
dotnet add package Masa.Contrib.Data.DistributedLock.Local --version 0.6.0-preview.10
- 註冊鎖,修改類
Program
builder.Services.AddLocalDistributedLock();//註冊本地鎖
- 如何使用鎖?修改類
Program
app.MapGet("lock", (IDistributedLock distributedLock) =>
{
using var @lock = distributedLock.TryGet("test");//獲取鎖
if (@lock != null)
{
//todo: 獲取鎖成功
return "success";
}
return "獲取超時";
});
通過DI獲取IDistributedLock
,並通過TryGet
方法獲取鎖,如果獲取鎖失敗,則返回null,如果返回到的物件不為null,則表明獲取鎖成功,最後在獲取鎖成功後寫自己的業務程式碼即可
TryGet
方法擁有以下引數
key
(string, 必須): 鎖的唯一名稱,可通過key來訪問不同的資源,執行不同的業務timeout
(TimeSpan): 等待獲取鎖的最大超時時間. 預設值為: TimeSpan.Zero(代表如果鎖已經被另一個應用程式擁有, 它不會等待.)
TryGetAsync
方法除了擁有TryGet
的所有引數之外,還擁有以下引數
cancellationToken
: 取消令牌可在觸發後取消操作
如果你選擇使用Medallion
,只需要選擇一種技術實現,並根據Readme
註冊鎖即可,在使用鎖上是沒有區別的
如何擴充套件其它的分散式鎖
-
新建類庫
Masa.Contrib.Data.DistributedLock.{分散式鎖名}
,並新增引用Masa.BuildingBlocks.Data.csproj
-
新建分散式鎖實現類
DefaultDistributedLock
,並實現IDistributedLock
public class DefaultDistributedLock : IDistributedLock
{
public IDisposable? TryGet(string key, TimeSpan timeout = default)
{
// 獲取鎖失敗則返回null,當資源被釋放時,主動釋放鎖, 無需人為手動釋放
throw new NotImplementedException();
}
public Task<IAsyncDisposable?> TryGetAsync(string key, TimeSpan timeout = default, CancellationToken cancellationToken = default)
{
//獲取鎖失敗則返回null,當資源被釋放時,主動釋放鎖, 無需人為手動釋放
throw new NotImplementedException();
}
}
- 新建類
ServiceCollectionExtensions
,註冊分散式鎖到服務集合
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddDistributedLock(this IServiceCollection services, Action<MedallionBuilder> builder)
{
services.TryAddSingleton<IDistributedLock, DefaultDistributedLock>();
return services;
}
}
小知識
為什麼TryGet
、TryGetAsync
方法的返回型別分別是IDisposable
、IAsyncDisposable
?
我們希望使用鎖可以足夠的簡單,在使用完鎖之後可以自動釋放鎖,而不是必須手動釋放,當返回型別為IDisposable
、IAsyncDisposable
時,使用完畢後會觸發Dispose
或DisposeAsync
,這樣一來就可以使得開發者可以忽略釋放鎖的邏輯
以本地鎖為例:
public class DefaultLocalDistributedLock : IDistributedLock
{
private readonly MemoryCache<string, SemaphoreSlim> _localObjects = new();
public IDisposable? TryGet(string key, TimeSpan timeout = default)
{
var semaphore = GetSemaphoreSlim(key);
if (!semaphore.Wait(timeout))
{
return null;
}
return new DisposeAction(semaphore);
}
//todo: 以下省略 TryGetAsync 方法
private SemaphoreSlim GetSemaphoreSlim(string key)
{
ArgumentNullOrWhiteSpaceException.ThrowIfNullOrWhiteSpace(key);
return _localObjects.GetOrAdd(key, _ => new SemaphoreSlim(1, 1));
}
}
internal class DisposeAction : IDisposable, IAsyncDisposable
{
private readonly SemaphoreSlim _semaphore;
public DisposeAction(SemaphoreSlim semaphore) => _semaphore = semaphore;
public ValueTask DisposeAsync()
{
_semaphore.Release();
return ValueTask.CompletedTask;
}
public void Dispose() => _semaphore.Release();
}
本章原始碼
Assignment09
http://github.com/zhenlei520/MasaFramework.Practice
開源地址
MASA.Framework:http://github.com/masastack/MASA.Framework
如果你對我們的 MASA Framework 感興趣,無論是程式碼貢獻、使用、提 Issue,歡迎聯絡我們
- WeChat:MasaStackTechOps
- QQ:7424099
- Blazor在IoT領域的前端實踐 @.NET開發者日
- MASA MAUI Plugin (十)iOS訊息推送(原生APNS方式)
- MASA MAUI Plugin (九)Android相簿多選照片(使用Android Jetpack套件庫)
- MASA MAUI Plugin (八)Android相簿多選照片(Intent 方式)
- MASA Stack 1.0 釋出會講稿——生態篇
- MASA Stack 1.0 釋出會講稿——實踐篇
- MASA Stack 1.0 釋出會講稿——產品篇
- MASA Stack 1.0 釋出會講稿——趨勢篇
- MASA MAUI Plugin (七)應用通知角標(小紅點)Android iOS
- .NET現代化應用開發 - CQRS&類目管理程式碼剖析
- MASA MAUI Plugin 安卓藍芽低功耗(二)藍芽通訊
- MASA MAUI Plugin 安卓藍芽低功耗(一)藍芽掃描
- MASA MAUI Plugin 安卓藍芽低功耗(二)藍芽通訊
- MASA MAUI Plugin 安卓藍芽低功耗(一)藍芽掃描
- MASA Framework的分散式鎖設計
- MAUI Masa Blazor 開發介面跟隨系統主題切換的App
- MAUI Masa Blazor 開發介面跟隨系統主題切換的App
- MAUI Masa Blazor 開發帶自動更新功能的安卓App
- 開篇-開啟全新的.NET現代應用開發體驗
- 怎麼樣的框架對於開發者是友好的?