代码环境
- 仓库:https://github.com/springvortex/spring-cloud-alibaba.git
- 分支:
release/v1.0.0- JDK:
21- Spring Boot:
4.1.0
商品购买最初直接注入 RedissonClient,能跑通,但业务代码被迫知道锁的获取、中断、续期、解锁异常等细节。这次提交把这些细节下沉到公共模板:
public interface DistributedLockTemplate {
<T> T execute(String lockKey, Duration waitTime, Supplier<T> action);
<T> T execute(String lockKey, Duration waitTime,
String timeoutMessage, Supplier<T> action);
}
业务侧变成:
distributedLockFactory.getTemplate().execute(
PURCHASE_LOCK_KEY_PREFIX + goodsId,
LOCK_WAIT,
"当前购买人数过多,请稍后再试",
() -> purchaseInLock(goodsId, request));
默认实现由配置选择:
zjc:
distributed-lock:
provider: redis
mysql:
table-name: t_distributed_lock
lease-time: 30s
retry-interval: 100ms
当前支持三种 Provider:
| Provider | 状态 | 说明 |
|---|---|---|
redis |
可用 | Redisson 可重入锁,不传租期时由看门狗续期 |
mysql |
可用 | 租约表、唯一键、行事务、重入计数和后台续期 |
zookeeper |
占位 | 显式抛出未实现,保留后续接入 Curator 的位置 |
MySQL 实现使用 lock_key 唯一键和 SELECT ... FOR UPDATE 完成抢锁,每个线程有独立 owner,支持重入计数;持有期间按租期的三分之一续期,进程崩溃后等待租约到期释放。表名通过白名单正则校验后才拼接 SQL,避免配置注入。
公共模块中的 Redisson、JDBC 和事务依赖都是 optional。自动配置根据已有 Bean 装配可用实现:有 RedissonClient 就提供 Redis 模板,有 JdbcTemplate 和事务管理器才提供 MySQL 模板。不需要锁或没有对应基础设施的服务,不会被强制引入依赖。
这个抽象的边界也清楚:它解决“用统一方式拿锁和释放锁”,不把业务事务包进锁模板。商品购买仍在回调里使用 TransactionTemplate,数据库条件更新依旧是最后兜底。
经验总结
当锁的语义开始包含等待、超时、中断和释放异常时,业务代码不应该直接面对具体客户端 API。统一模板保留业务语义,把存储差异和释放细节留在基础设施层。
评论
评论由 GitHub Discussions 承载,需要 GitHub 账号登录。