环境:SpringBoot3.2.5
lock4j是一个分布式锁组件,其提供了多种不同的支持以满足不同性能和环境的需求。底层通过Spring AOP技术实现,而该切面的优先级是最高的,也就是说当你的环境中有多个切面时(如:声明式事务),也不会导致失效问题。该组件具有如下2个特性:
接下来将详细介绍基于Redis的Lock4J使用。
<properties> <lock4j.version>2.2.7</lock4j.version></properties><dependency> <groupId>com.baomidou</groupId> <artifactId>lock4j-redis-template-spring-boot-starter</artifactId> <version>${lock4j.version}</version></dependency>
如果你想基于redisson或者是zookeeper实现,那么你只需要引入对应的包即可。
<!-- redisson --><artifactId>lock4j-redisson-spring-boot-starter</artifactId><!-- zookeeper --><artifactId>lock4j-zookeeper-spring-boot-starter</artifactId>
spring: data: redis: timeout: 10000 connectTimeout: 20000 host: 127.0.0.1 password: xxxooo #如果你是基于zookeeper实现,那么做如下配置 coordinate: zookeeper: zkServers: 127.0.0.1:2181,...
进过以上的配置后接下来你就可以通过注解的方式使用分布式锁了。
@Servicepublic class StorageService { private final StorageRepository storageRepository ; public StorageService(StorageRepository storageRepository) { this.storageRepository = storageRepository ; } @Lock4j public void deductStorage(Long storageId, int count) { // TODO }}
使用非常简单,只需要在你需要上锁的方法上添加@Lock4j注解即可。而这里将使用默认行为:默认获取锁超时3秒,30秒锁过期。
自定义锁key
在上面示例中没有自定义@Lock4j注解的任何属性;那么,将会使用默认的key生成方式,上面的代码将生成如下key。
# 前缀 + ":" + 完全限定类名+方法名+# (这里的#是固定的,如果你自定义了key,会在后面继续拼接)lock4j:com.pack.test.lock4j.StorageServicedeductStorage#
当我们自定义了key后如下:
@Lock4j(key = "#storageId")public void deductStorage(Long storageId, int count)
key支持Spring SpEL表达式,如上将生成如下的key
# 方法调用storageService.deductStorage(1, 2) ;lock4j:com.pack.test.lock4j.StorageServicedeductStorage#1
你也可以不使用SpEL表达式。不使用SpEL表达式那么就需要使用如下语法
@Lock4j(keys = "'storageId'")public void deductStorage(...) {}
没有使用单引号程序将会报错。
设置过期时间
默认获取锁超时3秒,30秒锁过期,可以通过如下属性配置过期时间
@Lock4j(keys = {"#storageId"}, expire = 3000, acquireTimeout = 3000)public void deductStorage(Long storageId, int count) {}
expire: 锁过期时间(毫秒);注:锁过期时间必须要大于业务处理时间。acquireTimeout: 获取锁超时时间(毫秒)
以上是Lock4j的基本用法;下面将介绍Lock4j其它高级用法。
全局统一配置
lock4j: acquire-timeout: 3000 expire: 30000 primary-executor: com.baomidou.lock.executor.RedisTemplateLockExecutor lock-key-prefix: lock4j
primary-executor:配置加锁的实现方式;默认顺序是:
redisson > redisTemplate > zookeeper;如果你的环境中这3个都引入了,那么就是按照这个顺序,因为定义他们时使用了@Order注解声明顺序。lock-key-prefix:#锁前缀,如上面示例看到的lock4j为默认值。
自定义执行器(加锁)
我们可以通过实现LockExecutor接口定义自己的加锁实现,比如基于MySQL实现。如下:
@Componentpublic class JdbcLockExecutor implements LockExecutor<String> { private final JdbcTemplate jdbcTemplate ; public JdbcLockExecutor(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate ; } @Override public String acquire(String lockKey, String lockValue, long expire, long acquireTimeout) { // TODO; 获取锁 return null ; } @Override public boolean releaseLock(String key, String value, String lockInstance) { // TODO; 释放锁 return false; }}
注:这里必须要注册为Spring Bean。否则将无法获取。使用如下:
@Lock4j(executor = JdbcLockExecutor.class)public void deductStorage(Long storageId, int count) {}
加锁时会根据这里配置的executor Class对象获取Spring容器对应的bean对象。
自定义key生成器
Lock4j默认的锁key生成器为DefaultLockKeyBuilder,我们可以通过LockKeyBuilder接口实现自己的key生成方式
@Componentpublic class PackLockKeyBuilder implements LockKeyBuilder { @Override public String buildKey(MethodInvocation invocation, String[] definitionKeys) { // TODO; 生成key return null; }}
注:这里也必须注册为bean对象。使用如下:
@Lock4j(keyBuilderStrategy = PackLockKeyBuilder.class)public void deductStorage(Long storageId, int count) {}
内部会通过你这里配置的Class对象获取对应的Spring Bean实例对象。
自定义获取锁失败策略
当获取锁失败时如何进行处理?默认实现是DefaultLockFailureStrategy通过自定义LockFailureStrategy,实现自己的逻辑。与上面的套路一样都需要注册为bean。
@Componentpublic class PackLockFailureStrategy implements LockFailureStrategy { @Override public void onLockFailure(String key, Method method, Object[] arguments) { // TODO }}
使用如下:
@Lock4j(failStrategy = PackLockFailureStrategy.class)public void deductStorage(Long storageId, int count) {}
注:这里你也可以不指定failStrategy属性,会自动从容器中查找对应的实现Bean。
非注解实现方式(手动加锁/释放锁)
你也可以通过注入LockTemplate对象,由自己来完成加锁和释放锁的动作。
private final LockTemplate lockTemplate ; public StorageService(LockTemplate lockTemplate) { this.lockTemplate = lockTemplate ; } public void deductStorage(Long storageId, int count) { String key = "xxxx" ; final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedisTemplateLockExecutor.class); if (null == lockInfo) { throw new RuntimeException("业务处理中,请稍后再试") ; } // 获取锁成功,处理业务 try { // TODO } finally { // 释放锁 lockTemplate.releaseLock(lockInfo) ; } }}
手动加锁方式,一般适用于需要更细粒度控制锁边界,否则没必要。
本文链接:http://www.28at.com/showinfo-26-100722-0.htmlSpringBoot强大的分布式锁组件Lock4j,支持多种实现
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 编程范式,建议掌握这五种!
下一篇: 电商并发减库存设计,如何做到不超卖