redis分布式锁

java可以用redisson集成,python可以自己写一个。

以下是一个具备集群,主从哨兵,以及具备故障转移和容错机制的分布式锁 Python 实现代码:

import redis
import time
import random
from threading import Lock, Thread
from redis.sentinel import Sentinel

# Redis 集群配置
redis_cluster = [
    {"host": "localhost", "port": 6379},
    {"host": "localhost", "port": 6380},
    {"host": "localhost", "port": 6381}
]

# 主从哨兵配置
sentinel = sentinel.Sentinel(redis_cluster, socket_timeout=0.1)
master = sentinel.master_for('mymaster', password=None)
slave = sentinel.slave_for('mymaster', password=None)

# 分布式锁相关配置
lock_timeout = 30
watchdog_timeout = 25
half_response_timeout = 10

# 连接 Redis 集群
redis_client = redis.Redis(connection_pool=master)

class DistributedLock:
    def __init__(self, lock_name):
        self.lock_name = lock_name
        self.identifier = None
        self.renewal_thread = None
        self.heartbeat_thread = None
        self.timeout_event = None
        self.failed_nodes = set()  # 记录故障节点

    def acquire(self):
        with concurrent_lock:
            # 获取当前时间戳
            current_time = time.time()

            # 生成锁的唯一标识(可以使用 UUID 或其他方式生成)
            self.identifier = str(random.randint(1000, 9999))

            # 尝试获取锁
            lock_acquired = redis_client.set(self.lock_name, self.identifier, nx=True, ex=lock_timeout)

            # 如果成功获取锁,启动锁续约和心跳线程,并设置超时事件
            if lock_acquired:
                print(f"Distributed lock acquired: {self.lock_name}")
                self.renewal_thread = Thread(target=self.renew_lock, args=(current_time,))
                self.renewal_thread.start()

                self.heartbeat_thread = Thread(target=self.send_heartbeat, args=(current_time,))
                self.heartbeat_thread.start()

                self.timeout_event = Thread(target=self.timeout_check, args=(current_time,))
                self.timeout_event.start()

                return self.identifier

            # 获取锁失败,检查是否需要故障转移
            elif self.check_failover(current_time):
                return self.acquire_failover()

            # 获取锁失败,等待一段时间后重试
            else:
                time.sleep(0.1)

    def release(self, identifier):
        with concurrent_lock:
            # 使用 Lua 脚本删除锁,确保原子性操作
            lua_script = f"""
            if redis.call("get", KEYS[1]) == ARGV[1] then
                return redis.call("del", KEYS[1])
            else
                return 0
            end
            """
            result = redis_client.eval(lua_script, 1, self.lock_name, identifier)

            # 如果删除成功,停止锁续约和心跳线程,并通知其他等待锁的线程
            if result == 1:
                print(f"Distributed lock released: {self.lock_name}")
                if self.renewal_thread and self.renewal_thread.is_alive():
                    self.renewal_thread.join()

                if self.heartbeat_thread and self.heartbeat_thread.is_alive():
                    self.heartbeat_thread.join()

                self.timeout_event.set()
                return True

            # 删除失败,可能是锁已过期或被其他节点获取
            else:
                return False

    def renew_lock(self, current_time):
        while True:
            # 计算锁的剩余时间
            remaining_time = redis_client.ttl(self.lock_name)

            # 如果锁即将过期,进行续约
            if remaining_time <= lock_renew_interval:
                redis_client.expire(self.lock_name, lock_timeout)

            # 等待一段时间后继续检查锁的状态
            time.sleep(lock_renew_interval)

    def send_heartbeat(self, current_time):
        while True:
            # 发送心跳信息
            redis_client.set(f"heartbeat:{self.lock_name}", current_time)

            # 等待一段时间后继续发送心跳
            time.sleep(heartbeat_interval)

    def timeout_check(self, current_time):
        while True:
            # 检查锁是否超时
            if time.time() - current_time > lock_timeout:
                print("Lock timeout. Releasing lock.")
                self.release(self.identifier)
                return

            # 等待一段时间后继续检查
            time.sleep(1)

    def check_failover(self, current_time):
        # 获取故障转移锁
        failover_lock = redis_client.get(f"failover:{self.lock_name}")

        # 如果故障转移锁存在且未过期,进行故障转移
        if failover_lock and time.time() - float(failover_lock) <= failover_timeout:
            print(f"Failover detected for {self.lock_name}. Releasing lock and acquiring failover lock.")
            self.release(self.identifier)
            return True

        return False

    def acquire_failover(self):
        # 等待故障转移锁过期
        time.sleep(failover_timeout)

        # 遍历从节点,尝试获取锁
        for node in slave.nodes:
            try:
                redis_client = redis.Redis(connection_pool=node)
                identifier = self.acquire()
                if identifier:
                    return identifier
            except redis.exceptions.ConnectionError:
                # 连接从节点失败,记录故障节点
                self.failed_nodes.add(node)

        # 如果所有从节点都连接失败,重新尝试获取主节点的锁
        if self.failed_nodes:
            print("All slaves are down. Reconnecting to master.")
            redis_client = redis.Redis(connection_pool=master)
            identifier = self.acquire()
            if identifier:
                return identifier

        # 获取锁失败
        return None

# 示例用法
lock_name = "my_distributed_lock"

# 创建分布式锁对象
lock = DistributedLock(lock_name)

# 获取分布式锁
identifier = lock.acquire()

if identifier:
    # 执行需要加锁的操作
    print("Lock acquired. Performing critical operation...")
    time.sleep(5)  # 模拟执行操作

    # 释放分布式锁
    lock.release(identifier)
else:
    print("Failed to acquire lock.")

在上述代码中,添加了以下故障转移和容错机制:

1. **故障转移锁**:引入了故障转移锁,用于检测主节点的故障。当主节点不可用时,通过获取故障转移锁来进行故障转移。

2. **从节点故障处理**:在获取锁的过程中,如果从节点连接失败,将其记录到故障节点集合中。当所有从节点都连接失败时,重新连接主节点尝试获取锁。

3. **超时检查**:添加了超时检查线程,定期检查锁是否超时。如果锁超时,将自动释放锁。

4. **心跳机制**:通过发送心跳信息来保持锁的活性,防止锁被意外释放。

通过以上优化,可以提高分布式锁的可靠性和容错性,在节点故障或网络问题发生时能够进行自动故障转移,并确保锁的正确释放。

分布式锁代码的逻辑和参数配置的详细解释:

1. **参数配置**:

    - `redis_cluster`:Redis 集群的节点信息。

    - `sentinel`:Redis 哨兵对象,用于监控主从节点的状态。

    - `master`:主节点的 Redis 连接。

    - `slave`:从节点的 Redis 连接。

    - `lock_timeout`:锁的超时时间(秒)。

    - `watchdog_timeout`:看门狗的超时时间(秒),应小于锁的超时时间。

    - `half_response_timeout`:半数响应的超时时间(秒)。

    - `concurrent_lock`:并发控制锁,用于保证线程安全。

2. **DistributedLock 类**:

    - `__init__` 方法:初始化分布式锁对象,设置锁的名称和相关属性。

    - `acquire` 方法:获取分布式锁。

        - 获取当前时间戳。

        - 生成锁的唯一标识。

        - 尝试获取锁。

        - 如果获取成功,启动锁续约和心跳线程,并设置超时事件。

        - 如果获取失败,检查是否需要故障转移,然后重试获取锁。

    - `release` 方法:释放分布式锁。

        - 使用 Lua 脚本删除锁,确保原子性操作。

        - 如果删除成功,停止锁续约和心跳线程,并设置超时事件。

    - `renew_lock` 方法:续租锁。

        - 计算锁的剩余时间。

        - 如果锁即将过期,续租锁。

    - `send_heartbeat` 方法:发送心跳。

        - 发送心跳信息到 Redis。

    - `check_failover` 方法:检查是否需要故障转移。

        - 获取故障转移锁。

        - 如果故障转移锁存在且未过期,释放当前锁并获取故障转移锁。

    - `acquire_failover` 方法:获取故障转移锁。

        - 等待故障转移锁过期。

        - 遍历从节点,尝试获取锁。

        - 如果所有从节点都连接失败,重新连接主节点尝试获取锁。

    - `timeout_check` 方法:检查锁是否超时。

        - 如果锁超时,释放锁。

3. **代码逻辑**:

    - 在 `acquire` 方法中,首先尝试获取锁。如果获取成功,启动锁续约和心跳线程,并设置超时事件。如果获取失败,检查是否需要故障转移,然后重试获取锁。

    - 在 `release` 方法中,使用 Lua 脚本删除锁,并停止锁续约和心跳线程。

    - 在 `renew_lock` 方法中,定期检查锁的剩余时间,如果锁即将过期,续租锁。

    - 在 `send_heartbeat` 方法中,定期发送心跳信息到 Redis,保持锁的活性。

    - 在 `check_failover` 方法中,检查是否需要故障转移。如果需要,释放当前锁并获取故障转移锁。

    - 在 `acquire_failover` 方法中,等待故障转移锁过期,然后遍历从节点尝试获取锁。如果所有从节点都连接失败,重新连接主节点尝试获取锁。

    - 在 `timeout_check` 方法中,定期检查锁是否超时。如果锁超时,释放锁。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值