dlock

package
v0.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package dlock 定义跨进程分布式锁与选主(leader election)的后端无关接口。

背景:beauty 支持多实例部署,但很多场景要求"同一时刻只有一个实例做某件事"—— 最典型是 Cron:多实例各自跑一遍会导致任务重复执行(发重奖励、重复扣款、 重复生成报表)。pkg/keyedmutex 是进程内锁,解决不了跨进程互斥;这里补上 跨进程版本的两个原语:

  • Locker:分布式互斥锁,Lock/TryLock 拿到 Lock 后独占 key,Unlock 释放;
  • Elector:持续选主,参选 key 下的 leader 身份,当选时收到回调,失去 leader(网络分区/进程崩溃/主动放弃)时回调的 ctx 被取消。

本包只定义接口 + 提供内存实现(单进程内多 goroutine 竞争,供开发/测试/ 单实例部署使用)。真实跨进程后端见 pkg/infra/etcd(基于 etcd 官方 client/v3/concurrency 包的 Session+Mutex / Session+Election,不重新发明 分布式锁算法)。遵循 beauty 纯标准库约定,核心接口零依赖。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterElector

func RegisterElector(scheme string, fn ElectorFactory)

RegisterElector 注册一个 scheme 对应的 Elector 工厂。 在各 infra 子包的 init() 中调用,重复注册同一 scheme 会 panic。

func RegisterLocker

func RegisterLocker(scheme string, fn LockerFactory)

RegisterLocker 注册一个 scheme 对应的 Locker 工厂。 在各 infra 子包的 init() 中调用,重复注册同一 scheme 会 panic。

Types

type Elector

type Elector interface {
	// Run 参选 key 下的 leader 身份,阻塞运行直到 ctx 被取消(返回 ctx.Err())。
	//
	// 当选时,以 leaderCtx 调用 onElected——leaderCtx 在"失去 leader 身份"时被
	// cancel(包括:outer ctx 取消、租约/会话失效、主动放弃)。onElected 应把
	// leaderCtx 当作"仍是 leader"的凭证:长期工作应持续检查 leaderCtx.Done(),
	// 一旦触发就必须停止对应工作(此时可能已有新 leader 当选)。
	//
	// onElected 返回后(通常因 leaderCtx.Done()),若 outer ctx 仍存活,Run 会
	// 自动重新参选;直到 outer ctx 取消才真正退出并返回 ctx.Err()。
	Run(ctx context.Context, key string, onElected func(leaderCtx context.Context)) error
}

Elector 是跨进程选主器:多个实例参选同一个 key,任意时刻至多一个当选 leader。

func NewElector

func NewElector(rawURL string) (Elector, error)

NewElector 根据 DSN 构造一个 Elector(选主器),用法同 New。除 etcd/consul/redis 外,k8s 后端也注册了 Elector(基于 Lease 资源),postgres / mysql 后端基于 Advisory Lock 实现选主(session-level 锁随连接释放,无需额外 TTL),DSN 分别形如 "k8s://?namespace=prod&kubeconfig=/path"、 "postgres://user:pass@host:5432/db?retry=2s&heartbeat=5s" 和 "mysql://user:pass@host:3306/db?retry=2s&heartbeat=5s"。

func NewElectorAuto

func NewElectorAuto() (Elector, error)

NewElectorAuto 根据运行环境自动选择 Elector 后端:

  • 检测到 KUBERNETES_SERVICE_HOST → 使用 k8s Lease 选主 (需提前 import _ "github.com/rushteam/beauty/pkg/infra/k8s")
  • 否则 → 使用内存选主(单实例/本地开发)

Lease 资源名由调用方在 Elector.Run(ctx, key, ...) 时通过 key 参数指定, 不在此处配置。可通过 DLOCK_ELECTOR_URL 环境变量强制指定后端 DSN,覆盖自动检测。

type ElectorFactory

type ElectorFactory func(u *url.URL) (Elector, error)

ElectorFactory 从解析好的 URL 构造一个 Elector。scheme 由各 infra 子包在 init() 中注册。

type Lock

type Lock interface {
	// Unlock 释放锁。幂等:重复调用不应返回错误或产生副作用。
	Unlock(ctx context.Context) error
}

Lock 是一次成功获取的锁,持有期间独占对应 key。

type Locker

type Locker interface {
	// Lock 阻塞直到获得 key 的锁,或 ctx 被取消(此时返回 ctx.Err())。
	Lock(ctx context.Context, key string) (Lock, error)

	// TryLock 非阻塞尝试获取 key 的锁。已被占用返回 (nil, false, nil)。
	TryLock(ctx context.Context, key string) (Lock, bool, error)
}

Locker 是跨进程分布式互斥锁。

func New

func New(rawURL string) (Locker, error)

New 根据 DSN 构造一个 Locker,与 conf.New 的 scheme 工厂模式对齐:先 import 对应 的 infra 子包(触发 init 注册工厂),再按 DSN 选择后端,免去在业务代码里硬编码 具体实现。锁的 key 在调用 Lock/TryLock 时传入,不在 DSN 里。

各后端 DSN 由其工厂决定,通用 query 约定:prefix(key 前缀)、ttl(会话/租约 存活时间,如 15s)。示例:

dlock.New("etcd://127.0.0.1:2379/?ttl=10s&prefix=/beauty/dlock/")
dlock.New("consul://127.0.0.1:8500/?ttl=15s")
dlock.New("redis://:pass@127.0.0.1:6379/0?ttl=15s&retry=100ms")
dlock.New("postgres://user:pass@127.0.0.1:5432/db?sslmode=disable&prefix=beauty/dlock/")
dlock.New("mysql://user:pass@127.0.0.1:3306/db?prefix=beauty:dlock:&retry=2s")

注:k8s 后端只提供 Elector(client-go 是选主语义,没有互斥锁原语),用 NewElector。

type LockerFactory

type LockerFactory func(u *url.URL) (Locker, error)

LockerFactory 从解析好的 URL 构造一个 Locker。scheme 由各 infra 子包在 init() 中注册。

type Memory

type Memory struct {
	// contains filtered or unexported fields
}

Memory 是 Locker + Elector 的纯内存实现:多个 goroutine 竞争同一个 key, 语义等价"多实例竞争",供开发/测试/单实例部署使用。不跨进程——生产多实例 部署请用 pkg/infra/etcd 等真实后端。

零值不可用,用 NewMemory 构造。并发安全。

func NewMemory

func NewMemory() *Memory

NewMemory 创建内存 Locker/Elector。

func (*Memory) Lock

func (m *Memory) Lock(ctx context.Context, key string) (Lock, error)

Lock 实现 Locker。阻塞式获取,支持 ctx 取消(取消时不会拿到锁)。

func (*Memory) Run

func (m *Memory) Run(ctx context.Context, key string, onElected func(leaderCtx context.Context)) error

Run 实现 Elector:反复竞争 key 的锁,拿到即视为当选 leader,调用 onElected; onElected 返回或 ctx 取消即释放并重新竞选,直到 ctx 取消退出。

func (*Memory) TryLock

func (m *Memory) TryLock(_ context.Context, key string) (Lock, bool, error)

TryLock 实现 Locker。非阻塞。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL