LOADING

加载过慢请开启缓存 浏览器默认开启

分布式锁与看门狗机制

2025/7/8 算法

分布式锁与看门狗机制

  • 分布式锁是一种在分布式环境下协调多个节点共享资源访问的机制。它的目的是在分布式系统中实现对共享资源的互斥访问,确保在任何时候只有一个节点能够获得锁并对资源进行操作。

  • 在分布式系统中,看门狗机制通常用于检测和恢复系统的异常状态。对于分布式锁而言,它可以用于监控持有锁的线程或进程是否正常工作。例如,如果持有锁的线程在一段时间内没有响应或未能释放锁,看门狗会认为该线程可能已经失效,这时会强制释放锁以避免死锁。

代码分析

数据结构设计

type RedisLock struct {
    client      *redis.Client
    key         string
    value       string
    expiration  time.Duration // 锁的过期时间
    renewPeriod time.Duration // 看门狗续期周期
    ctx         context.Context
    cancelFunc  context.CancelFunc
}
  • client:Redis 客户端,用于与 Redis 服务器通信
  • key:锁的键名,在 Redis 中作为键存在
  • value:锁的值,使用 UUID 生成的唯一标识,确保只有锁的持有者才能释放锁
  • expiration:锁的过期时间,防止死锁
  • renewPeriod:看门狗续期的时间间隔,通常设置为过期时间的 1/3
  • ctx cancelFunc:用于控制看门狗协程的生命周期

锁的获取(Acquire 方法)

func (l *RedisLock) Acquire(blocking bool, timeout time.Duration) (bool, error) {
    ctx, cancel := context.WithTimeout(l.ctx, timeout)
    defer cancel()

    startTime := time.Now()

    for {
        // 使用SET命令原子性地获取锁
        set, err := l.client.SetNX(ctx, l.key, l.value, l.expiration).Result()
        if err != nil {
            return false, err
        }

        if set {
            // 获取锁成功,启动看门狗
            l.startWatchdog()
            return true, nil
        }

        if !blocking {
            return false, nil
        }

        if timeout > 0 && time.Since(startTime) >= timeout {
            return false, nil
        }

        // 短暂休眠避免频繁重试
        time.Sleep(100 * time.Millisecond)
    }
}

关键技术点:

  1. 原子操作:使用 Redis 的SetNX命令原子性地设置锁,等效于SET key value NX EX timeout
  2. 阻塞模式:通过循环尝试获取锁实现阻塞模式
  3. 超时控制:使用context.WithTimeout控制获取锁的最长时间
  4. 看门狗启动:获取锁成功后启动看门狗协程,自动续期锁

锁的释放(Release 方法)

func (l *RedisLock) Release() (bool, error) {
    // 停止看门狗
    if l.cancelFunc != nil {
        l.cancelFunc()
    }

    // 使用Lua脚本原子性地验证并释放锁
    luaScript := `
    if redis.call("GET", KEYS[1]) == ARGV[1] then
        return redis.call("DEL", KEYS[1])
    else
        return 0
    end
    `

    result, err := l.client.Eval(l.ctx, luaScript, []string{l.key}, l.value).Result()
    if err != nil {
        return false, err
    }

    return result.(int64) == 1, nil
}

关键技术点:

  1. 原子性验证与释放:使用 Lua 脚本确保验证锁持有者和释放锁的操作是原子性的
  2. 防止误释放:只有锁的持有者(value 匹配)才能释放锁
  3. 停止看门狗:释放锁前先停止看门狗协程,避免不必要的续期操作

看门狗自动续期机制

func (l *RedisLock) startWatchdog() {
    ctx, cancel := context.WithCancel(l.ctx)
    l.cancelFunc = cancel

    go func() {
        ticker := time.NewTicker(l.renewPeriod)
        defer ticker.Stop()

        for {
            select {
            case <-ticker.C:
                // 续期锁
                err := l.renew()
                if err != nil {
                    log.Printf("看门狗续期失败: key=%s, error=%v", l.key, err)
                    return
                }
            case <-ctx.Done():
                log.Printf("看门狗停止: key=%s", l.key)
                return
            }
        }
    }()
}

func (l *RedisLock) renew() error {
    // 使用Lua脚本验证并续期锁,确保操作的原子性
    luaScript := `
    if redis.call("GET", KEYS[1]) == ARGV[1] then
        return redis.call("PEXPIRE", KEYS[1], ARGV[2])
    else
        return 0
    end
    `

    // 将过期时间转换为毫秒
    expirationMs := int64(l.expiration / time.Millisecond)

    result, err := l.client.Eval(l.ctx, luaScript, []string{l.key}, l.value, expirationMs).Result()
    if err != nil {
        return err
    }

    if result.(int64) == 0 {
        return fmt.Errorf("锁已过期或被其他客户端持有: key=%s", l.key)
    }

    return nil
}

看门狗机制的关键技术点:

  1. 定期续期:按照renewPeriod设置的时间间隔定期续期锁
  2. 原子操作:使用 Lua 脚本确保只有锁的持有者才能续期
  3. 异常处理:续期失败时记录日志并退出协程
  4. 上下文控制:通过context控制协程的生命周期,确保锁释放后看门狗能停止

接口兼容性与最佳实践

  1. 实现 sync.Locker 接口
func (l *RedisLock) Lock() {
    _, err := l.Acquire(true, 0)
    if err != nil {
        log.Printf("获取锁失败: key=%s, error=%v", l.key, err)
    }
}

func (l *RedisLock) Unlock() {
    _, err := l.Release()
    if err != nil {
        log.Printf("释放锁失败: key=%s, error=%v", l.key, err)
    }
}

这使得该分布式锁可以无缝集成到使用sync.Locker接口的 Go 代码中,例如:

lock := NewRedisLock(rdb, "resource_lock", 30*time.Second)
lock.Lock()
defer lock.Unlock()
// 执行关键业务逻辑