diff --git a/lock4j/.gitignore b/lock4j/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/lock4j/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/lock4j/README.md b/lock4j/README.md new file mode 100644 index 0000000..c912aec --- /dev/null +++ b/lock4j/README.md @@ -0,0 +1,11 @@ +# demo-request-scope + +《基于ThreadLocal实现一个上下文管理组件》技术博客对应源码 + +https://juejin.cn/post/7153287656624324638 + +本文基于`ThreadLocal`原理,实现了一个上下文状态管理组件`Scope`,通过开启一个自定义的`Scope`,在`Scope`范围内,可以通过`Scope`各个方法读写数据; + +通过自定义线程池实现上下文状态数据的线程间传递; + +提出了一种基于`Filter`和`Scope`的`Request`粒度的上下文管理方案。 diff --git a/lock4j/pom.xml b/lock4j/pom.xml new file mode 100644 index 0000000..83d2dda --- /dev/null +++ b/lock4j/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.4 + + + com.example + demo-request-scope + 0.0.1-SNAPSHOT + demo-request-scope + Demo project for Spring Boot + + 8 + + + + + + + com.google.guava + guava + 30.1-jre + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.baomidou + lock4j-redisson-spring-boot-starter + 2.2.4 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/lock4j/src/main/java/com/example/demo/DemoRequestScopeApplication.java b/lock4j/src/main/java/com/example/demo/DemoRequestScopeApplication.java new file mode 100644 index 0000000..4c0d58d --- /dev/null +++ b/lock4j/src/main/java/com/example/demo/DemoRequestScopeApplication.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoRequestScopeApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoRequestScopeApplication.class, args); + } + +} diff --git a/lock4j/src/main/java/com/example/demo/controller/MockController.java b/lock4j/src/main/java/com/example/demo/controller/MockController.java new file mode 100644 index 0000000..098b3b9 --- /dev/null +++ b/lock4j/src/main/java/com/example/demo/controller/MockController.java @@ -0,0 +1,60 @@ +package com.example.demo.controller; +import com.baomidou.lock.LockInfo; +import com.baomidou.lock.LockTemplate; +import com.baomidou.lock.annotation.Lock4j; +import com.example.demo.executor.RedissonLockNewExecutor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * 分享一个分布式锁框架Lock4j + * 基于Spring AOP的声明式和编程式分布式锁,支持RedisTemplate、Redisson、Zookeeper。 + */ +@RestController +@RequestMapping("/mock") +public class MockController { + + @GetMapping("/lockMethod") + @Lock4j(keys = {"#key"}, acquireTimeout = 10, expire = 10000 + ,executor = RedissonLockNewExecutor.class + ) + public String lockMethod(@RequestParam String key) throws InterruptedException { + Thread.sleep(10000); + return key; + } + + @Autowired + private LockTemplate lockTemplate; + + @GetMapping("/lockMethodCostom") + public String lockMethodCostom(@RequestParam String key) { + LockInfo lock = lockTemplate.lock(key, 10000L, 2000L, RedissonLockNewExecutor.class); + if (lock == null) { + // 获取不到锁 + throw new RuntimeException("业务处理中,请稍后再试..."); + } + // 获取锁成功,处理业务 + try { + doBusiness(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + lockTemplate.releaseLock(lock); + } + return key; + } + + private void doBusiness() { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("woniuhaobang"); + } + + +} diff --git a/lock4j/src/main/java/com/example/demo/executor/CustomKeyBuilder.java b/lock4j/src/main/java/com/example/demo/executor/CustomKeyBuilder.java new file mode 100644 index 0000000..d96e219 --- /dev/null +++ b/lock4j/src/main/java/com/example/demo/executor/CustomKeyBuilder.java @@ -0,0 +1,19 @@ +package com.example.demo.executor; + +import com.baomidou.lock.DefaultLockKeyBuilder; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.stereotype.Component; + +/** + * 自定义分布式锁key生成规则 + * + * @author: woniu + * @since: 2023/4/15 15:46 + */ +@Component +public class CustomKeyBuilder extends DefaultLockKeyBuilder { + + public CustomKeyBuilder(BeanFactory beanFactory) { + super(beanFactory); + } +} \ No newline at end of file diff --git a/lock4j/src/main/java/com/example/demo/executor/GrabLockFailureStrategy.java b/lock4j/src/main/java/com/example/demo/executor/GrabLockFailureStrategy.java new file mode 100644 index 0000000..d966532 --- /dev/null +++ b/lock4j/src/main/java/com/example/demo/executor/GrabLockFailureStrategy.java @@ -0,0 +1,22 @@ +package com.example.demo.executor; + + +import com.baomidou.lock.LockFailureStrategy; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * 自定义抢占锁失败执行策略 + * + * @author: woniu + * @since: 2023/4/15 15:49 + */ +@Component +public class GrabLockFailureStrategy implements LockFailureStrategy { + + @Override + public void onLockFailure(String key, Method method, Object[] arguments) { + throw new RuntimeException("获取锁失败啦"); + } +} \ No newline at end of file diff --git a/lock4j/src/main/java/com/example/demo/executor/RedissonLockNewExecutor.java b/lock4j/src/main/java/com/example/demo/executor/RedissonLockNewExecutor.java new file mode 100644 index 0000000..498c92e --- /dev/null +++ b/lock4j/src/main/java/com/example/demo/executor/RedissonLockNewExecutor.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018-2022, baomidou (63976799@qq.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.demo.executor; +import com.baomidou.lock.executor.AbstractLockExecutor; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; + + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * redisson 重入锁 + * + * @author woniu + */ +@Slf4j +@RequiredArgsConstructor +public class RedissonLockNewExecutor extends AbstractLockExecutor { + + private final RedissonClient redissonClient; + + @Override + public boolean renewal() { + return true; + } + + @Override + public RLock acquire(String lockKey, String lockValue, long expire, long acquireTimeout) { + try { + final RLock lockInstance = redissonClient.getLock(lockKey); + final boolean locked = lockInstance.tryLock(acquireTimeout, expire, TimeUnit.MILLISECONDS); + return obtainLockInstance(locked, lockInstance); + } catch (InterruptedException e) { + return null; + } + } + + @Override + public boolean releaseLock(String key, String value, RLock lockInstance) { + if (lockInstance.isHeldByCurrentThread()) { + try { + return lockInstance.forceUnlockAsync().get(); + } catch (ExecutionException | InterruptedException e) { + return false; + } + } + return false; + } + +} diff --git a/lock4j/src/main/resources/application.properties b/lock4j/src/main/resources/application.properties new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lock4j/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/lock4j/src/main/resources/application.yml b/lock4j/src/main/resources/application.yml new file mode 100644 index 0000000..31435c0 --- /dev/null +++ b/lock4j/src/main/resources/application.yml @@ -0,0 +1,21 @@ +spring: + redis: + database: 0 + # Redis服务器地址 写你的ip + host: 192.168.56.10 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: 123456 + # 连接池最大连接数(使用负值表示没有限制 类似于mysql的连接池 + jedis: + pool: + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) 表示连接池的链接拿完了 现在去申请需要等待的时间 + max-wait: -1 + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接超时时间(毫秒) 去链接redis服务端 + timeout: 6000 \ No newline at end of file