基於Redis的分佈式鎖和Redlock算法

背景

在單進程的系統中,當存在多個線程能夠同時改變某個變量(可變共享變量)時,就須要對變量或代碼塊作同步,使其在修改這種變量時可以線性執行消除併發修改變量。linux

而同步的本質是經過鎖來實現的。爲了實現多個線程在一個時刻同一個代碼塊只能有一個線程可執行,那麼須要在某個地方作個標記,這個標記必須每一個線程都能看到,當標記不存在時能夠設置該標記,其他後續線程發現已經有標記了則等待擁有標記的線程結束同步代碼塊取消標記後再去嘗試設置標記。這個標記能夠理解爲鎖。redis

不一樣地方實現鎖的方式也不同,只要能知足全部線程都能看獲得標記便可。如 Java 中 synchronize 是在對象頭設置標記,Lock 接口的實現類基本上都只是某一個 volitile 修飾的 int 型變量其保證每一個線程都能擁有對該 int 的可見性和原子修改,linux 內核中也是利用互斥量或信號量等內存數據作標記。數據庫

除了利用內存數據作鎖其實任何互斥的都能作鎖(只考慮互斥狀況),如流水錶中流水號與時間結合作冪等校驗能夠看做是一個不會釋放的鎖,或者使用某個文件是否存在做爲鎖等。只須要知足在對標記進行修改能保證原子性和內存可見性便可。網絡

概念

1 什麼是分佈式?多線程

分佈式的 CAP 理論告訴咱們:併發

任何一個分佈式系統都沒法同時知足一致性(Consistency)、可用性(Availability)和分區容錯性(Partition tolerance),最多隻能同時知足兩項。async

目前不少大型網站及應用都是分佈式部署的,分佈式場景中的數據一致性問題一直是一個比較重要的話題。基於 CAP理論,不少系統在設計之初就要對這三者作出取捨。在互聯網領域的絕大多數的場景中,都須要犧牲強一致性來換取系統的高可用性,系統每每只須要保證最終一致性。分佈式

場景

分佈式場景ide

此處主要指集羣模式下,多個相同服務同時開啓.性能

在許多的場景中,咱們爲了保證數據的最終一致性,須要不少的技術方案來支持,好比分佈式事務、分佈式鎖等。不少時候咱們須要保證一個方法在同一時間內只能被同一個線程執行。在單機環境中,經過 Java 提供的併發 API 咱們能夠解決,可是在分佈式環境下,就沒有那麼簡單啦。

● 分佈式與單機狀況下最大的不一樣在於其不是多線程而是多進程。

● 多線程因爲能夠共享堆內存,所以能夠簡單的採起內存做爲標記存儲位置。而進程之間甚至可能都不在同一臺物理機上,所以須要將標記存儲在一個全部進程都能看到的地方。

什麼是分佈式鎖?

● 當在分佈式模型下,數據只有一份(或有限制),此時須要利用鎖的技術控制某一時刻修改數據的進程數。

● 與單機模式下的鎖不只須要保證進程可見,還須要考慮進程與鎖之間的網絡問題。(我以爲分佈式狀況下之因此問題變得複雜,主要就是須要考慮到網絡的延時和不可靠。。。一個大坑)

● 分佈式鎖仍是能夠將標記存在內存,只是該內存不是某個進程分配的內存而是公共內存如 Redis、Memcache。至於利用數據庫、文件等作鎖與單機的實現是同樣的,只要保證標記能互斥就行。

2 咱們須要怎樣的分佈式鎖?

能夠保證在分佈式部署的應用集羣中,同一個方法在同一時間只能被一臺機器上的一個線程執行。

這把鎖要是一把可重入鎖(避免死鎖)

這把鎖最好是一把阻塞鎖(根據業務需求考慮要不要這條)

這把鎖最好是一把公平鎖(根據業務需求考慮要不要這條)

有高可用的獲取鎖和釋放鎖功能

獲取鎖和釋放鎖的性能要好

代碼實現

代碼實現

public interface IDistributedLock

{

ILockResult Lock(string resourceKey);

ILockResult Lock(string resourceKey, TimeSpan expiryTime);

ILockResult Lock(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime);

ILockResult Lock(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime, CancellationToken cancellationToken);

Task LockAsync(string resourceKey);

Task LockAsync(string resourceKey, TimeSpan expiryTime);

Task LockAsync(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime);

Task LockAsync(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime, CancellationToken cancellationToken);

}

public interface ILockResult : IDisposable

{

string LockId { get; }

bool IsAcquired { get; }

int ExtendCount { get; }

}

class EndPoint:RedLock.RedisLockEndPoint
    {
        private readonly string _connectionString;
        public EndPoint(string connectionString)
        {
            _connectionString = connectionString;
            //139.196.40.252,password=xstudio,defaultDatabase=9
            var connection = connectionString.Split(',');
            var dict = new Dictionary<string, string>();
            foreach (var item in connection)
            {
                var keypar = item.Split('=');
                if (keypar.Length>1)
                {
                    dict[keypar[0]] = keypar[1];
                }
            }
            this.EndPoint = new System.Net.DnsEndPoint(connection[0], 6379);
            if (dict.TryGetValue("password", out string password))
            {
                this.Password = password;
            }
            if (dict.TryGetValue("defaultDatabase", out string defaultDatabase) && int.TryParse(defaultDatabase,out int database))
            {
                RedisDatabase = database;
            }
        }
    }

[Export(typeof(IDistributedLock))]
    class InnerLock : IDistributedLock
    {
        private static Lazy<RedLock.RedisLockFactory> _factory;

        static InnerLock()
        {
            _factory = new Lazy<RedisLockFactory>(() => new RedisLockFactory(new EndPoint(ConfigurationManager.AppSettings["Redis"])), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
        }
        public ILockResult Lock(string resourceKey)
        {
            return new LockResult(_factory.Value.Create(resourceKey, TimeSpan.FromDays(1)));
        }

        public ILockResult Lock(string resourceKey, TimeSpan expiryTime)
        {
            return new LockResult(_factory.Value.Create(resourceKey, expiryTime));
        }

        public ILockResult Lock(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime)
        {
            return new LockResult(_factory.Value.Create(resourceKey, expiryTime, waitTime, retryTime));
        }

        public ILockResult Lock(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime, CancellationToken cancellationToken)
        {
            return new LockResult(_factory.Value.Create(resourceKey, expiryTime, waitTime, retryTime, cancellationToken));
        }

        public async Task<ILockResult> LockAsync(string resourceKey)
        {
            var result = await _factory.Value.CreateAsync(resourceKey, TimeSpan.FromDays(1));
            return new LockResult(result);
        }

        public async Task<ILockResult> LockAsync(string resourceKey, TimeSpan expiryTime)
        {
            var result = await _factory.Value.CreateAsync(resourceKey, expiryTime);
            return new LockResult(result);
        }

        public async Task<ILockResult> LockAsync(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime)
        {
            var result = await _factory.Value.CreateAsync(resourceKey, expiryTime, waitTime, retryTime);
            return new LockResult(result);
        }

        public async Task<ILockResult> LockAsync(string resourceKey, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime, CancellationToken cancellationToken)
        {
            var result = await _factory.Value.CreateAsync(resourceKey, expiryTime, waitTime, retryTime, cancellationToken);
            return new LockResult(result);
        }
    }

    class LockResult : ILockResult
    {
        private IRedisLock _lock;
        public LockResult(IRedisLock redisLock)
        {
            _lock = redisLock;
        }

        public string LockId => _lock.LockId;

        public bool IsAcquired => _lock.IsAcquired;

        public int ExtendCount => _lock.ExtendCount;

        public void Dispose()
        {
            _lock.Dispose();
        }
    }複製代碼
相關文章
相關標籤/搜索