Địa chỉ mã nguồn trên GitHub samcook/RedLock.net - Triển khai thuật toán Redlock bằng C#
Cấu trúc mã nguồn Các lớp chính: RedLockFactory, RedLock
Khởi tạo và giải phóng khóa Phân tích từ lớp RedLock:
private readonly ICollection<RedisConnection> redisNodes; // Tập hợp node Redis
private readonly int requiredNodes; // Số lượng cần thiết để đạt quorum
private readonly int retryCount;
private readonly int retryDelayMs;
private readonly TimeSpan lockExpiry; // Thời gian hết hạn khóa
private readonly TimeSpan? waitTimeout; // Thời gian chờ tối đa
private readonly TimeSpan? retryInterval; // Khoảng cách giữa các lần thử
Tính toán tham số
requiredNodes = redisNodes.Count / 2 + 1; // Công thức quorum
retryCount = retryConfig?.RetryCount ?? DefaultRetryCount;
retryDelayMs = retryConfig?.RetryDelayMs ?? DefaultRetryDelayMs;
Khởi tạo khóa
internal static RedLock Create(
ILogger<RedLock> logger,
ICollection<RedisConnection> nodes,
string resourceKey,
TimeSpan expiry,
TimeSpan? waitTime = null,
TimeSpan? retryInterval = null,
RedLockRetryConfiguration retryConfig = null,
CancellationToken? token = null)
{
var locker = new RedLock(
logger, nodes, resourceKey, expiry,
waitTime, retryInterval, retryConfig, token);
locker.Start();
return locker;
}
Khởi động tiến trình khóa
private void Start()
{
if (HasRetryConfig())
{
var timer = Stopwatch.StartNew();
while (!IsLocked && timer.Elapsed <= waitTimeout.Value)
{
(Status, Summary) = AcquireLock();
if (!IsLocked) DelayAndCheckCancellation();
}
}
else
{
(Status, Summary) = AcquireLock();
}
logger.LogInfo($"Trạng thái khóa: {Status} ({Summary}), {ResourceKey} ({LockToken})");
if (IsLocked) StartAutoRenewal();
}
Quy trình lấy khóa
private (LockStatus, LockSummary) AcquireLock()
{
var summary = new LockSummary();
for (var i = 0; i < retryCount; i++)
{
cancellationToken.ThrowIfCancellationRequested();
var attempt = i + 1;
logger.LogDebug($"Thử khóa lần {attempt}/{retryCount}: {ResourceKey} ({LockToken})");
var stopwatch = Stopwatch.StartNew();
summary = ExecuteLockAttempt();
var validity = CalculateValidity(stopwatch);
logger.LogDebug($"Đã khóa {summary.Acquired}/{redisNodes.Count} node, quorum: {requiredNodes}, thời gian hiệu lực: {validity}");
if (summary.Acquired >= requiredNodes && validity > 0)
{
return (LockStatus.Success, summary);
}
ReleaseAllLocks();
if (i < retryCount - 1) RandomDelay();
}
return (DetermineFailureStatus(summary), summary);
}
Cơ chế khóa phân tán
private LockSummary ExecuteLockAttempt()
{
var results = new ConcurrentBag<LockResult>();
Parallel.ForEach(redisNodes, node =>
results.Add(LockSingleNode(node)));
return BuildSummary(results);
}
private LockResult LockSingleNode(RedisConnection node)
{
var redisKey = FormatKey(node.KeyFormat, ResourceKey);
var host = GetHost(node.Connection);
try
{
var redisResult = node.Connection.GetDatabase(node.DbIndex)
.StringSet(redisKey, LockToken, lockExpiry, When.NotExists, CommandFlags.DemandMaster);
return redisResult ? LockResult.Success : LockResult.Conflict;
}
catch
{
return LockResult.Error;
}
}
Giải phóng khóa
private void ReleaseAllLocks()
{
extendUnlockSemaphore.Wait();
try
{
Parallel.ForEach(redisNodes, ReleaseSingleNode);
}
finally
{
extendUnlockSemaphore.Release();
}
}
private void ReleaseSingleNode(RedisConnection node)
{
var redisKey = FormatKey(node.KeyFormat, ResourceKey);
try
{
node.Connection.GetDatabase(node.DbIndex)
.ScriptEvaluate(UnlockScript, new RedisKey[] {redisKey},
new RedisValue[] {LockToken}, CommandFlags.DemandMaster);
}
catch { }
}
Script Lua giải phóng khóa
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
Trạng thái khóa
public enum LockStatus
{
Unlocked, // Chưa khóa
Success, // Khóa thành công
NoQuorum, // Không đạt quorum
Conflict, // Bị xung đột
Expired // Hết thời gian hiệu lực
}
Nguyên lý hoạt động
Khởi tạo đối tượng RedLock với tham số cấu hình Gọi phương thức Start() để bắt đầu quy trình khóa Thử thiết lập khóa trên tất cả node Redis với TTL xác định Kiểm tra điều kiện quorum (đạt tối thiểu N/2+1 node thành công) Giải phóng khóa trên tất cả node khi hoàn tất hoặc xảy ra lỗi
]]>