Documentation
¶
Overview ¶
Package lockx 定义分布式阻塞与非阻塞锁契约,提供获取、执行和释放的通用流程。 业务依赖 Locker(等待获取)或 TryLocker(单次尝试),初始化时选择具体后端。 当前由 redislock 提供固定租期的阻塞与非阻塞锁。
需要复用配置时通过 New(backend) 绑定为 Executor,调用 executor.Do/TryDo 无需再次传入后端。 NewNamed(name, backend) 创建命名执行器,NewDefault(name, backend) 同时将其设为默认; Default() 与对应的 Named(name) 返回同一个实例,New 和 NewNamed 不隐式设置默认实例。 空白名称、重复名称、第二个默认实例和 nil 后端返回错误,失败不占用名称或默认位置; 获取未登记的实例会 panic。启动阶段完成全部登记后再运行业务,不支持配置热切换。 执行器不拥有后端资源,也不提供 Close;应用须先停止业务,再关闭共享客户端。 名称仅选择配置,不参与 key 构造;同一 Redis 数据库的相同 key 在不同执行器之间仍然互斥。 使用 biz/bootstrap 时可由 MustRegisterRedisLock 按代码声明,在 Redis 就绪后统一装配。
以下示例需导入 context 和 github.com/bpcoder16/pixiu/biz/lockx; locker 由启动流程注入,confirm 为应用提供的业务函数:
func confirmOrder(
ctx context.Context,
locker lockx.Locker,
orderID string,
confirm func(context.Context) error,
) error {
return lockx.Do(ctx, locker, "orders:confirm:"+orderID, confirm)
}
使用 TryLocker 时通过 TryDo 调用:
func tryConfirmOrder(
ctx context.Context,
locker lockx.TryLocker,
confirm func(context.Context) error,
) (bool, error) {
return lockx.TryDo(ctx, locker, "orders:confirm:123", confirm)
}
批量非阻塞执行使用 TryDoMany 或 executor.TryDoMany,Backend 已要求实现 TryManyLocker:
func tryConfirmBatch(
ctx context.Context,
keys []string,
confirm func(context.Context) error,
) (bool, error) {
return lockx.Default().TryDoMany(ctx, keys, confirm)
}
keys 必须非空且不能包含空白 key;复制并按原值去重,不修剪、不改大小写。 整批获取成功后才执行一次回调;任一冲突时整批失败。单 key 与批量使用相同 key 时互斥。 Backend 内嵌 Locker、TryLocker 和 TryManyLocker,三种能力在编译期约束,执行器直接调用。 仅增加非阻塞批量能力,不提供 DoMany / LockMany。应用应限制单次 key 数量,避免过大批次占用后端。 TryDo / TryDoMany 的 false、nil 表示竞争失败,true 只表示进入过回调,不代表业务已提交。 上述执行函数同步执行回调最多一次,工作期限取请求与 Lock.Deadline 的较早者。 获取阶段等待预算不会带入工作;内置 Redis 租期锁不自动续租。 回调返回或 panic 后使用独立的 1 秒期限尽力释放;正常返回合并错误,保留原始 panic。
手工调用 Lock/TryLock 时应立即登记释放,租期句柄的 Deadline 包含成功尝试耗时。 获取 context 取消不会自动释放已交付的句柄。Unlock 原子校验持有身份, 内置实现对不再持有、到期或重复释放幂等返回 nil,真实下游错误仍返回。 释放的 nil 不代表仍持有锁,业务本身的租期超时仍会返回;取消不能强制停止业务。
不同后端或协议的同名锁互不排斥;同一资源的竞争者必须使用相同实现。 迁移前先停止旧竞争者并完成旧临界区。锁不替代幂等、事务或数据端版本约束, 租期过期后的旧任务仍可能执行。
Index ¶
- Variables
- func Do(ctx context.Context, locker Locker, key string, fn func(context.Context) error) error
- func TryDo(ctx context.Context, locker TryLocker, key string, ...) (bool, error)
- func TryDoMany(ctx context.Context, locker TryManyLocker, keys []string, ...) (bool, error)
- type Backend
- type Executor
- func (e *Executor) Do(ctx context.Context, key string, fn func(context.Context) error) error
- func (e *Executor) TryDo(ctx context.Context, key string, fn func(context.Context) error) (bool, error)
- func (e *Executor) TryDoMany(ctx context.Context, keys []string, fn func(context.Context) error) (bool, error)
- type Lock
- type Locker
- type TryLocker
- type TryManyLocker
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNotHeld = errors.New("lockx: lock not held")
ErrNotHeld 表示实现确认本次锁已过期、已释放或已由其他执行者持有。 保留用于兼容旧后端;内置实现的 Unlock 将这些状态作为幂等完成并返回 nil。
Functions ¶
func Do ¶
Do 等待获取锁,同步执行 fn 并释放;获取失败不执行 fn。 工作期限取 ctx 与句柄期限的较早者,取消不能强制终止 fn。 释放脱离请求取消并独立限时 1 秒;正常路径合并错误,panic 时继续原始 panic。
Example ¶
package main
import (
"context"
"github.com/bpcoder16/pixiu/biz/lockx"
)
func main() {
confirm := func(ctx context.Context, locker lockx.Locker, fn func(context.Context) error) error {
return lockx.Do(ctx, locker, "orders:confirm:123", fn)
}
_ = confirm
}
Output:
func TryDo ¶
func TryDo(ctx context.Context, locker TryLocker, key string, fn func(context.Context) error) (bool, error)
TryDo 尝试获取锁,成功后同步执行 fn 并释放;竞争失败返回 false、nil。 executed 表示是否进入过 fn,不表示业务是否提交成功;其余语义与 Do 相同。
Example ¶
package main
import (
"context"
"github.com/bpcoder16/pixiu/biz/lockx"
)
func main() {
// 业务接收统一接口,初始化位置决定 Redis 或其他实现。
tryConfirm := func(
ctx context.Context,
locker lockx.TryLocker,
orderID string,
confirm func(context.Context) error,
) (bool, error) {
return lockx.TryDo(ctx, locker, "orders:lock:confirm:"+orderID, confirm)
}
_ = tryConfirm
}
Output:
Types ¶
type Backend ¶
type Backend interface {
Locker
TryLocker
TryManyLocker
}
Backend 同时支持单 key 阻塞、单 key 非阻塞及整批非阻塞获取;共享使用时须支持并发调用。
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor 绑定可复用的锁后端,调用时只需传入请求 context、业务 key 和回调。 不保存单次锁句柄,不创建或关闭后端资源;零值不可用,须通过 New 系列函数构造。
func Default ¶
func Default() *Executor
Default 返回显式登记的默认执行器,与对应 Named(name) 是同一实例;未登记时 panic。 登记表不拥有后端资源,调用方须在关闭后端之前停止使用执行器。
func NewDefault ¶
NewDefault 创建并按 name 登记执行器,同时设为默认;已有默认实例时返回错误。 创建失败不占用名称或默认位置;其余约束与 NewNamed 相同。
func NewNamed ¶
NewNamed 创建并登记命名执行器;名称不能为空白,重复名称返回错误且不替换已有实例。 应在启动阶段完成全部登记后再运行业务;后端的资源始终由调用方关闭。
Example ¶
package main
import (
"context"
"github.com/bpcoder16/pixiu/biz/lockx"
)
func main() {
// 后端由启动流程创建;两个后端可以共享 Redis 并持有不同的租期配置。
configure := func(normal, longTask lockx.Backend) error {
if _, err := lockx.NewDefault("default", normal); err != nil {
return err
}
_, err := lockx.NewNamed("long-task", longTask)
return err
}
run := func(ctx context.Context, key string, work func(context.Context) error) error {
return lockx.Named("long-task").Do(ctx, key, work)
}
_ = configure
_ = run
}
Output:
func (*Executor) TryDo ¶
func (e *Executor) TryDo(ctx context.Context, key string, fn func(context.Context) error) (bool, error)
TryDo 只尝试一次;竞争失败返回 false、nil,executed 只表示是否进入过 fn。 其余语义沿用包级 TryDo;执行器名称不参与 key 的构造。
func (*Executor) TryDoMany ¶
func (e *Executor) TryDoMany(ctx context.Context, keys []string, fn func(context.Context) error) (bool, error)
TryDoMany 通过绑定的 Backend 整批获取,沿用包级 TryDoMany 的执行与清理语义。
Example ¶
批量竞争只尝试一次;业务 key 由应用按资源构造。
package main
import (
"context"
"github.com/bpcoder16/pixiu/biz/lockx"
)
func main() {
publish := func(ctx context.Context, keys []string, work func(context.Context) error) (bool, error) {
return lockx.Default().TryDoMany(ctx, keys, work)
}
_ = publish
}
Output:
type Lock ¶
type Lock interface {
// Deadline 返回保守的本地工作期限,不包含获取前的竞争等待。
// 没有固定期限时返回零值、false;有限租期包含成功尝试的耗时。
Deadline() (time.Time, bool)
// Unlock 原子校验本次身份,允许并发调用,不得删除其他持有者的锁。
// 已过期、不再持有或重复释放作为幂等完成,返回 nil。
// 参数、取消和真实下游错误仍须按实现契约返回。
// 幂等释放成功不代表业务仍在有效租期内。
Unlock(ctx context.Context) error
}
Lock 是一次独立获取的锁句柄,持有身份由实现保存。