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 ¶
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 ¶
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 ¶
ElectorFactory 从解析好的 URL 构造一个 Elector。scheme 由各 infra 子包在 init() 中注册。
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 ¶
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 ¶
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 构造。并发安全。