分佈式鎖

需求

實現一個分佈式鎖

問題

  1. 分佈式鎖與傳統的鎖有和區別
    關於這個問題相關的解釋不是很多,沒看到官方解釋和對比;民間的解釋是這樣的,我們所說的傳統的鎖,一般指的是線程鎖,而分佈式鎖指的是進程鎖。
    個人對民間的這種稱呼,不是很認同。單主機下的進程鎖,和多主機下的進程鎖(分佈式鎖)應該還是區別,至少實現上來說多主機鎖,需要比單主機進程所多考慮一點跨主機的網絡通信問題。這個網絡通信是對進程鎖的設計,在不同的場景下,還是有不同的區別的。

我的思路

  1. 其實我開始想到的第一個點就是,我們現在使用的鎖,利用python的fcntl庫(文件鎖)實現的一種鎖,支持區分讀寫鎖、阻塞鎖、非阻塞鎖;文件鎖屬於進程鎖。
  2. 我們的web程序是多進程的,Apache開的多個httpd進程,多個進程競爭自願。使用文件鎖能夠滿足普通的需求。
  3. 當我被別人問起,分佈式鎖的時候,我就很詫異,那我用這個本身不就能當作一個分佈式鎖來用麼,不過需要加一些網絡接口(比如restful api),讀完接下來下面業界常用的實現方法,再來看我自己的這個疑問。

業界常用做法

今天只說一種最常用的,利用redis實現的分佈式鎖,redis本身(redis操作)的原子性,對鎖的支持是天然的。
(1)獲取鎖的時候,使用setnx加鎖,並使用expire命令爲鎖添加一個超時時間,超過該時間則自動釋放鎖,鎖的value值爲一個隨機生成的UUID,通過此在釋放鎖的時候進行判斷。
(2)獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
(3)釋放鎖的時候,a.通過UUID判斷是不是該鎖,若是該鎖,b.則執行delete進行鎖釋放。(注意ab操作要保證原子性,否則,可能存在這樣的情況,執行步驟a------>鎖超時釋放--------->另一個任務加鎖成功----------->執行步驟b;這樣的後果,就是鎖的添加釋放錯亂了;可以利用redis的事務實現多個操作的原子性)

redis分佈式鎖參考
鎖參考

代碼實現

#連接redis
import time
import uuid
from threading import Thread

import redis

redis_client = redis.Redis(host="localhost",
                           port=6379,
                           # password=123,
                           db=10)

#獲取一個鎖
# lock_name:鎖定名稱
# acquire_time: 客戶端等待獲取鎖的時間
# time_out: 鎖的超時時間
def acquire_lock(lock_name, acquire_time=10, time_out=10):
    """獲取一個分佈式鎖"""
    identifier = str(uuid.uuid4())
    end = time.time() + acquire_time
    lock = "string:lock:" + lock_name
    while time.time() < end:
        if redis_client.setnx(lock, identifier):
            # 給鎖設置超時時間, 防止進程崩潰導致其他進程無法獲取鎖
            redis_client.expire(lock, time_out)
            return identifier
        elif not redis_client.ttl(lock):
            redis_client.expire(lock, time_out)
        time.sleep(0.001)
    return False

#釋放一個鎖
def release_lock(lock_name, identifier):
    """通用的鎖釋放函數"""
    lock = "string:lock:" + lock_name
    pip = redis_client.pipeline(True)
    while True:
        try:
            pip.watch(lock)
            lock_value = redis_client.get(lock)
            if not lock_value:
                return True

            if lock_value.decode() == identifier:
                pip.multi()
                pip.delete(lock)
                pip.execute()
                return True
            pip.unwatch()
            break
        except redis.excetions.WacthcError:
            pass
    return False
發佈了125 篇原創文章 · 獲贊 31 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章