什麼是鎖?什麼是分散式鎖?它們之間有什麼樣的關係?
加鎖(lock)是2018年公佈的電腦科學技術名詞,是指將控制變數置位,控制共用資源不能被其他執行緒存取。通過加鎖,可以確保在同一時刻只有一個執行緒在存取被鎖住的程式碼片段,我們在單機部署時可使用最簡單的加鎖完成資源的獨享,如:
public class Program
{
private static readonly object Obj = new { };
public static void Main()
{
lock (obj)
{
//同一時刻只有一個執行緒可以存取
}
}
}
但隨著業務發展的需要,原單體單機部署的系統被部署成分散式叢集系統後,原來的並行控制策略失效,為了解決這個問題就需要引入分散式鎖,那分散式鎖應該具備哪些條件?
分散式鎖是特定於實現的,目前MasaFramework提供了兩個實現,分別是Local
、Medallion
,下面會介紹如何設定並使用它們
是基於SemaphoreSlim
實現的,它不是真正的分散式鎖,我們建議你在開發和測試環境中使用它,不需要聯網也不會與其他人衝突
是基於DistributedLock實現的分散式鎖,它提供了很多種技術的實現,包括Microsoft SQL Server
、Postgresql
、MySQL 或 MariaDB
、Oracle
、Redis
、Azure blob
、Apache ZooKeeper
、鎖檔案
、作業系統全域性WaitHandles(Windows)
,我們只需要任選一種實現即可,目前Medallion
提供的分散式鎖並不支援可重入性,點選瞭解原因
以本地鎖單應用鎖為例:
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
https://github.com/zhenlei520/MasaFramework.Practice
MASA.Framework:https://github.com/masastack/MASA.Framework
MASA.EShop:https://github.com/masalabs/MASA.EShop
MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor
如果你對我們的 MASA Framework 感興趣,無論是程式碼貢獻、使用、提 Issue,歡迎聯絡我們
本文來自部落格園,作者:磊_磊,轉載請註明原文連結:https://www.cnblogs.com/zhenlei520/p/16694025.html
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結,否則保留追究法律責任的權利