Py = > Python version redis distributed lock simple implementation

Time:2020-10-26

The definition code is as follows

import redis
import contextlib
import pickle
import os, socket, threading


class RedisLock:
    def __init__(self, lock_name, host='', port=6379, db=0):
        self.lock_name = lock_name
        self.redis = redis.Redis(connection_pool=redis.ConnectionPool(host=host, port=port, db=db))

    def acquire_lock(self, lock_id, expire=None):
        lock_id = lock_id if lock_id else self.get_lock_id()
        return True if self.redis.set(self.lock_name, pickle.dumps(lock_id), nx=True, ex=expire) else False

        # Above 1 line code can replace with follow codes to debug
        # if self.redis.set(self.lock_name, pickle.dumps(lock_id), nx=True, ex=expire):
        #     print('Lock Succeed')
        #     return True
        # else:
        #     print('Lock Failed')
        #     return False

    def release_lock(self, lock_id=None):
        lock_id = lock_id if lock_id else self.get_lock_id()
        if lock_id == pickle.loads(self.redis.get(self.lock_name)):
            self.redis.delete(self.lock_name)
            # print('Unlock Succeed')
            return True
        else:
            # print('Unlock Failed')
            return False

    @contextlib.contextmanager
    def lock(self, lock_id=None, expire=None):
        if not self.acquire_lock(lock_id, expire):
            exit(0)
        yield self
        self.release_lock(lock_id)

    def get_lock_id(self):
        """ hostname+processID+threadName"""
        return f'{socket.gethostname()}{os.getpid()}{threading.current_thread().name}'
    

The call code is as follows

redis_ Lock = redislock ('lockname ', host = your IP'), the first anonymous parameter must be passed as the key of redis
with redis_lock.lock() as lock:
    print('You Can Do Something Here')

Note the instructions

1. The comment part is the code I used to debug when I wrote it. At last, I replaced it with concise syntax

2. Because locks are mutually exclusive, we choose the NX parameter of set() to implement it,
    Nx parameter: I always remember (read as not exist) = = = > does not exist
    understand:
        If it does not exist, it will be added; if it exists, it will not be added.
        If there is no lock, add a lock. If there is a lock, it will not be locked.

3. The ex parameter of set method can be used to set the expiration time instead of the expire method

4. Redis has many command variants. Setx =, setx =, setx =
   However, "set() is a better instruction" and can be used as much as possible for the following reasons:
       "The advantage of set instruction is that set has atomicity", which can avoid resource competition while solving resource competition
   
5. I use the decorator version of the context manager to encapsulate the code, so when calling, I can use "with statement"

6. with redis_ lock.lock () as lock, "lock() here you can specify two parameters yourself":
    lock_ Id = none ා this is a unique identifier that distinguishes different threads. It is (host name + process ID + thread name) by default, and can be self generated
    Expiration = none ා expiration time in seconds

7. We need to pay attention to one point. Communication with redis is in binary form. So I'm working on lock inside the code_ ID "pickle serialization"
   Of course, if it is a string, it is OK to use encode() and decode().