ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

MASA Framework的分布式锁设计

2022-09-16 14:30:17  阅读:319  来源: 互联网

标签:return MASA 获取 Framework key semaphore public 分布式


前言

什么是锁?什么是分布式锁?它们之间有什么样的关系?

什么是锁

加锁(lock)是2018年公布的计算机科学技术名词,是指将控制变量置位,控制共享资源不能被其他线程访问。通过加锁,可以确保在同一时刻只有一个线程在访问被锁住的代码片段,我们在单机部署时可使用最简单的加锁完成资源的独享,如:

public class Program
{
    private static readonly object Obj = new { };

    public static void Main()
    {
        lock (obj)
        {
            //同一时刻只有一个线程可以访问
        }
    }
}

什么是分布式锁

但随着业务发展的需要,原单体单机部署的系统被部署成分布式集群系统后,原来的并发控制策略失效,为了解决这个问题就需要引入分布式锁,那分布式锁应该具备哪些条件?

  • 原子性:在分布式环境下,一个方法在同一个时间点只能被一台机器下的一个线程所执行,防止数据资源的并发访问,避免数据不一致情况
  • 高可用:具备自动失效机制,防止死锁,获取锁后如果出现错误,并且无法释放锁,则使用租约一段时间后自动释放锁
  • 阻塞性:具备非阻塞锁特性(没有获取到锁时直接返回获取锁失败,不会长时间因等待锁导致阻塞)
  • 高性能:高性能的获取锁与释放锁
  • 可重入性:具备可重入特性,在同一线程外层函数获得锁之后,内层方法会自动获取锁

实现

分布式锁是特定于实现的,目前MasaFramework提供了两个实现,分别是LocalMedallion,下面会介绍如何配置并使用它们

本地锁

是基于SemaphoreSlim实现的,它不是真正的分布式锁,我们建议你在开发和测试环境中使用它,不需要联网也不会与其他人冲突

Medallion

是基于DistributedLock实现的分布式锁,它提供了很多种技术的实现,包括Microsoft SQL ServerPostgresqlMySQL 或 MariaDBOracleRedisAzure blobApache ZooKeeper锁文件操作系统全局WaitHandles(Windows),我们只需要任选一种实现即可,目前Medallion提供的分布式锁并不支持可重入性,点击了解原因

快速入门

本地锁单应用锁为例:

  1. 新建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
  1. 注册锁,修改类Program
builder.Services.AddLocalDistributedLock();//注册本地锁
  1. 如何使用锁?修改类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注册锁即可,在使用锁上是没有区别的

如何扩展其它的分布式锁

  1. 新建类库Masa.Contrib.Data.DistributedLock.{分布式锁名},并添加引用Masa.BuildingBlocks.Data.csproj

  2. 新建分布式锁实现类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();
    }
}
  1. 新建类ServiceCollectionExtensions,注册分布式锁到服务集合
public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddDistributedLock(this IServiceCollection services, Action<MedallionBuilder> builder)
    {
        services.TryAddSingleton<IDistributedLock, DefaultDistributedLock>();
        return services;
    }
}

小知识

为什么TryGetTryGetAsync方法的返回类型分别是IDisposableIAsyncDisposable

我们希望使用锁可以足够的简单,在使用完锁之后可以自动释放锁,而不是必须手动释放,当返回类型为IDisposableIAsyncDisposable时,使用完毕后会触发DisposeDisposeAsync,这样一来就可以使得开发者可以忽略释放锁的逻辑

以本地锁为例:

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 Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

  • WeChat:MasaStackTechOps
  • QQ:7424099

标签:return,MASA,获取,Framework,key,semaphore,public,分布式
来源: https://www.cnblogs.com/MASA/p/16699776.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有