分佈式鎖方案html
MySQL
Version 樂觀鎖
Redis
SETNX PX命令,LUA腳本(推薦)
Spring Integration Redis
引入依賴
dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-integration' implementation 'org.springframework.integration:spring-integration-redis' }
添加配置(Spring Boot自動裝配)
spring: redis: host: localhost port: 6379
@Bean public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) { RedisLockRegistry redisLockRegistry = new RedisLockRegistry(redisConnectionFactory, "redisLockRegistry"); return redisLockRegistry; }
編碼演示
private RedisLockRegistry redisLockRegistry; public void process(String orderNo) { Lock lock = redisLockRegistry.obtain("orderNo:" + orderNo); boolean result = false; try { result = lock.tryLock(1000L, TimeUnit.MICROSECONDS); if (result) { //todo } } catch (InterruptedException e) { // log.error("lock error", e); } finally { if (result) { lock.unlock(); } } } public RedisLockRegistry getRedisLockRegistry() { return redisLockRegistry; } @Autowired public void setRedisLockRegistry(RedisLockRegistry redisLockRegistry) { this.redisLockRegistry = redisLockRegistry; }
Redission
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.12.5</version> </dependency>
Zookeeper
臨時有序節點 +【Watcher機制】
Apache Curator 自行查閱
Spring Integration ZooKeeper
引入依賴
dependencies { implementation 'org.springframework.boot:spring-boot-starter-integration' implementation 'org.springframework.integration:spring-integration-zookeeper' implementation 'org.apache.curator:curator-recipes:4.3.0' }
添加配置
spring: zookeeper: server: localhost:2181
@Value("${spring.zookeeper.server}") private String connectString; @Bean public CuratorFramework curatorFramework() { RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3); CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient(connectString,retryPolicy); curatorFramework.start(); return curatorFramework; } @Bean public ZookeeperLockRegistry zookeeperLockRegistry(CuratorFramework curatorFramework) { ZookeeperLockRegistry zookeeperLockRegistry = new ZookeeperLockRegistry(curatorFramework); return zookeeperLockRegistry; }
編碼演示
private ZookeeperLockRegistry zookeeperLockRegistry; public void process(String orderNo) { Lock lock = zookeeperLockRegistry.obtain("orderNo:" + orderNo); boolean result = false; try { result = lock.tryLock(1000L, TimeUnit.MICROSECONDS); if (result) { //todo } } catch (InterruptedException e) { // log.error("lock error", e); } finally { if (result) { lock.unlock(); } } } public ZookeeperLockRegistry getZookeeperLockRegistry() { return zookeeperLockRegistry; } @Autowired public void setZookeeperLockRegistry(ZookeeperLockRegistry zookeeperLockRegistry) { this.zookeeperLockRegistry = zookeeperLockRegistry; }
使用Spring AOP和SpEL 自定義註解
註解類
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DistributedLock { String expression() default ""; long expire() default 60000L; TimeUnit timeUnit() default TimeUnit.SECONDS; }
AOP切面
private RedisLockRegistry redisLockRegistry; @Pointcut("@annotation(DistributedLock)") public void distributedLockAspect() { } @Around(value = "distributedLockAspect()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { Class targetClass = pjp.getTarget().getClass(); String methodName = pjp.getSignature().getName(); Class[] parameterTypes = ((MethodSignature) pjp.getSignature()).getMethod().getParameterTypes(); Method method = targetClass.getMethod(methodName, parameterTypes); Object[] arguments = pjp.getArgs(); DistributedLock annotation = method.getAnnotation(DistributedLock.class); String lockKey = arguments.length > 0 && !StringUtils.isEmpty(annotation.expression()) ? getLockKey(annotation, arguments) : targetClass.getName() + ":" + methodName; Lock lock = redisLockRegistry.obtain(lockKey); boolean result = false; try { result = lock.tryLock(annotation.expire(), annotation.timeUnit()); if (result) { return pjp.proceed(); } } catch (InterruptedException e) { // log.error("lock error", e); } finally { if (result) { lock.unlock(); } } throw new LockException("lock error"); } private String getLockKey(DistributedLock annotation, Object[] arguments) { ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build(); context.setVariable("arg", arguments); return parser.parseExpression(annotation.expression()).getValue(context, String.class); }
使用栗子
/** * 使用第一個參數 * * @param orderNo */ @DistributedLock(expression = "#arg[0]") public void process1(String orderNo) { //todo log.info("working...."); } /** * 使用第二個參數的name屬性 * * @param orderNo * @param user */ @DistributedLock(expression = "#arg[1].name") public void process1(String orderNo, User user) { //todo log.info("working...."); } /** * 使用第一個參數和第二個參數的name屬性 * * @param orderNo * @param user */ @DistributedLock(expression = "#arg[0] + #arg[1].name") public void process2(String orderNo, User user) { //todo log.info("working...."); } /** * 使用常量 * * @param orderNo * @param user */ @DistributedLock(expression = "'test3'") public void process3(String orderNo, User user) { //todo log.info("working...."); }