環境: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.www897cc.com/showinfo-26-100722-0.htmlSpringBoot強大的分布式鎖組件Lock4j,支持多種實現
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 編程范式,建議掌握這五種!
下一篇: 電商并發減庫存設計,如何做到不超賣