分布式锁Lock4J 使用总结

Lok4j 简介

lock4j是一个分布式锁组件,其提供了多种不同的支持以满足不同性能和环境的需求。

立志打造一个简单但富有内涵的分布式锁组件。

特点

  • 简单易用,功能强大,扩展性强。
  • 支持redission,redisTemplate,zookeeper。可混用,支持扩展

Docker 安装Redis

请参考文章:Docker 安装Redis

Docker 安装ZooKeeper

请参考文章:Docker 安装Zookeeper

SpringBoot 集成Lock4j

Lock4j 之Reids 版本

pom.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringBootCase</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringBoot-Lock4J</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <mybatisplus-spring-boot-starter.version>3.4.3</mybatisplus-spring-boot-starter.version>
        <mysql-spring-boot-starter.version>8.0.19</mysql-spring-boot-starter.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatisplus-spring-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-spring-boot-starter.version}</version>
        </dependency>
        <!--添加开源分布式锁Lock4j-->
        <!--若使用redisTemplate作为分布式锁底层,则需要引入-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
            <version>2.2.4</version>
        </dependency>
    </dependencies>

</project>

application.yml 之Redis 配置

复制代码
server:
  port: 8086
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.43.10:3306/bill?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
    username: root
    password: 123456
    hikari:
      minimum-idle: 10
      maximum-pool-size: 20
      idle-timeout: 500000
      max-lifetime: 540000
      connection-timeout: 60000
      connection-test-query: select 1
  redis:
    database: 0
    host: 192.168.43.10
    port: 6379
    jedis:
      pool:
        max-active: 200
        max-wait: -1
        max-idle: 10
        min-idle: 0
    timeout: 6000

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: cn.zzg.mybatisplus.entity

controller 使用Lock4j 注解,实现分布式锁功能

复制代码
package cn.zzg.lock.controller;

import com.baomidou.lock.annotation.Lock4j;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/lock4j")
public class Lock4JController {

    @GetMapping("/lockMethod")
    @Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000)
    public String lockMethod(@RequestParam String key){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return new String(key);
    }

}

Lock4j 之Redission版本

pom.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringBootCase</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringBoot-Lock4JRedission</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--若使用redisson作为分布式锁底层,则需要引入-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
            <version>2.2.4</version>
        </dependency>
    </dependencies>


</project>

application.yml 配置文件

复制代码
server:
  port: 8089
spring:
  redis:
    database: 0
    host: 192.168.43.10
    port: 6379
    jedis:
      pool:
        max-active: 200
        max-wait: -1
        max-idle: 10
        min-idle: 0
    timeout: 6000

controller 使用Lock4j 注解,实现分布式锁功能

复制代码
package cn.zzg.lock4j.controller;

import com.baomidou.lock.annotation.Lock4j;
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;

@RestController
@RequestMapping("/lock4j")
public class Lock4JController {

    @GetMapping("/lockMethod")
    @Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000)
    public String lockMethod(@RequestParam String key){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return new String(key);
    }

}

Lock4j 之ZooKeeper 版本

pom.xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringBootCase</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringBoot-Lock4jZookeeper</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--添加开源分布式锁Lock4j-->
        <!--若使用Zookeeper作为分布式锁底层,则需要引入-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>lock4j-zookeeper-spring-boot-starter</artifactId>
            <version>2.2.4</version>
        </dependency>
    </dependencies>

</project>

application.yml 之zookeeper配置

复制代码
server:
  port: 8087
spring:
  coordinate:
    zookeeper:
      zkServers: 192.168.43.10:2181

controller 使用Lock4j 注解,实现分布式锁功能

复制代码
package cn.zzg.lock.controller;

import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.lock.executor.ZookeeperLockExecutor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/lock4j")
public class Lock4JController {

    @GetMapping("/lockMethod")
    @Lock4j(keys = {"#key"}, acquireTimeout = 1000, expire = 10000, executor = ZookeeperLockExecutor.class)
    public String lockMethod(@RequestParam String key){
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return new String(key);
    }

}

Lock4J 注解详解

Lock4J 高级使用

1、配置获取锁超时时间和锁过期时间。

复制代码
lock4j:
  acquire-timeout: 3000 #默认值3s,可不设置
  expire: 30000 #默认值30s,可不设置
  primary-executor: com.baomidou.lock.executor.RedisTemplateLockExecutor #默认redisson>redisTemplate>zookeeper,可不设置
  lock-key-prefix: lock4j #锁key前缀, 默认值lock4j,可不设置

acquire-timeout /排队时长,超出这个时间退出队列,并抛出超时异常。

expire 锁过期时间 。 主要是防止死锁。默认30秒是为了兼容绝大部分场景。

2、自定义执行器。

前提条件必须继承抽象类:com.baomidou.lock.executor.AbstractLockExecutor<T>

ZooKeeper 版本执行器之ZookeeperLockExecutor

复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.lock.executor;

import java.util.concurrent.TimeUnit;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ZookeeperLockExecutor extends AbstractLockExecutor<InterProcessMutex> {
    private static final Logger log = LoggerFactory.getLogger(ZookeeperLockExecutor.class);
    private final CuratorFramework curatorFramework;

    public InterProcessMutex acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
        if (!CuratorFrameworkState.STARTED.equals(this.curatorFramework.getState())) {
            log.warn("instance must be started before calling this method");
            return null;
        } else {
            String nodePath = "/curator/lock4j/%s";

            try {
                InterProcessMutex mutex = new InterProcessMutex(this.curatorFramework, String.format(nodePath, lockKey));
                boolean locked = mutex.acquire(acquireTimeout, TimeUnit.MILLISECONDS);
                return (InterProcessMutex)this.obtainLockInstance(locked, mutex);
            } catch (Exception var10) {
                return null;
            }
        }
    }

    public boolean releaseLock(String key, String value, InterProcessMutex lockInstance) {
        try {
            lockInstance.release();
            return true;
        } catch (Exception var5) {
            log.warn("zookeeper lock release error", var5);
            return false;
        }
    }

    public ZookeeperLockExecutor(final CuratorFramework curatorFramework) {
        this.curatorFramework = curatorFramework;
    }
}

Redis 版本执行器之RedisTemplateLockExecutor

复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.lock.executor;

import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

public class RedisTemplateLockExecutor extends AbstractLockExecutor<String> {
    private static final Logger log = LoggerFactory.getLogger(RedisTemplateLockExecutor.class);
    private static final RedisScript<String> SCRIPT_LOCK = new DefaultRedisScript("return redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2])", String.class);
    private static final RedisScript<String> SCRIPT_UNLOCK = new DefaultRedisScript("if redis.call('get',KEYS[1]) == ARGV[1] then return tostring(redis.call('del', KEYS[1])==1) else return 'false' end", String.class);
    private static final String LOCK_SUCCESS = "OK";
    private final StringRedisTemplate redisTemplate;

    public String acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
        String lock = (String)this.redisTemplate.execute(SCRIPT_LOCK, this.redisTemplate.getStringSerializer(), this.redisTemplate.getStringSerializer(), Collections.singletonList(lockKey), new Object[]{lockValue, String.valueOf(expire)});
        boolean locked = "OK".equals(lock);
        return (String)this.obtainLockInstance(locked, lock);
    }

    public boolean releaseLock(String key, String value, String lockInstance) {
        String releaseResult = (String)this.redisTemplate.execute(SCRIPT_UNLOCK, this.redisTemplate.getStringSerializer(), this.redisTemplate.getStringSerializer(), Collections.singletonList(key), new Object[]{value});
        return Boolean.parseBoolean(releaseResult);
    }

    public RedisTemplateLockExecutor(final StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
}

3、自定义Key生成器

前提条件必须实现:com.baomidou.lock.LockKeyBuilder 接口

默认Key生成器:DefaultLockKeyBuilder

复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.lock;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;

public class DefaultLockKeyBuilder implements LockKeyBuilder {
    private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
    private static final ExpressionParser PARSER = new SpelExpressionParser();
    private BeanResolver beanResolver;

    public DefaultLockKeyBuilder(BeanFactory beanFactory) {
        this.beanResolver = new BeanFactoryResolver(beanFactory);
    }

    public String buildKey(MethodInvocation invocation, String[] definitionKeys) {
        Method method = invocation.getMethod();
        return definitionKeys.length <= 1 && "".equals(definitionKeys[0]) ? "" : this.getSpelDefinitionKey(definitionKeys, method, invocation.getArguments());
    }

    protected String getSpelDefinitionKey(String[] definitionKeys, Method method, Object[] parameterValues) {
        StandardEvaluationContext context = new MethodBasedEvaluationContext((Object)null, method, parameterValues, NAME_DISCOVERER);
        context.setBeanResolver(this.beanResolver);
        List<String> definitionKeyList = new ArrayList(definitionKeys.length);
        String[] var6 = definitionKeys;
        int var7 = definitionKeys.length;

        for(int var8 = 0; var8 < var7; ++var8) {
            String definitionKey = var6[var8];
            if (definitionKey != null && !definitionKey.isEmpty()) {
                String key = (String)PARSER.parseExpression(definitionKey).getValue(context, String.class);
                definitionKeyList.add(key);
            }
        }

        return StringUtils.collectionToDelimitedString(definitionKeyList, ".", "", "");
    }
}

4、自定义锁获取失败策略

前提条件必须实现:com.baomidou.lock.LockFailureStrategy 接口

默认失败策略:DefaultLockFailureStrategy

复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.lock;

import com.baomidou.lock.exception.LockFailureException;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultLockFailureStrategy implements LockFailureStrategy {
    private static final Logger log = LoggerFactory.getLogger(DefaultLockFailureStrategy.class);
    protected static String DEFAULT_MESSAGE = "request failed,please retry it.";

    public DefaultLockFailureStrategy() {
    }

    public void onLockFailure(String key, Method method, Object[] arguments) {
        throw new LockFailureException(DEFAULT_MESSAGE);
    }
}

5、手动获取/释放锁

通用方法:

复制代码
@Service
public class CommonService {
    @Autowired
    private LockTemplate lockTemplate;

    public void commonLock(String bizId) {
        // 各种查询操作 不上锁
        // ...
        // 获取锁
        final LockInfo lockInfo = lockTemplate.lock(bizId, 30000L, 5000L, RedissonLockExecutor.class);
        if (null == lockInfo) {
            throw new RuntimeException("业务处理中,请稍后再试");
        }
        // 获取锁成功,处理业务
        try {
            System.out.println("执行普通方法 , 当前线程:" + Thread.currentThread().getName());
        } finally {
            //释放锁
            lockTemplate.releaseLock(lockInfo);
        }
        //结束
    }
}

参考资料

官网地址:Lock4J

相关推荐
小的~~2 小时前
面试被问分布式锁,我差点语塞…直到搞懂了Redis、ZooKeeper和etcd的“三国杀”
redis·分布式·面试
武科大许志伟3 小时前
从并行进化到分布式进化计算读 A Survey on Distributed Evolutionary Computation
人工智能·分布式·演化计算
国科安芯16 小时前
ASL706S:让 MCU 不再“失忆跑飞“的守护芯片
分布式·单片机·嵌入式硬件·安全·fpga开发·架构
阿无,17 小时前
RabbitMQ面试题
分布式·rabbitmq
盛世宏博智慧档案19 小时前
全国100个分布式机房环境温湿度智能监测系统建设方案
分布式·机房·温湿度
六bring个六20 小时前
分布式设备连接对端失败问题分析
分布式·分布式软总线
天涯明月19931 天前
ray深度研究报告
大数据·人工智能·分布式·ray
Rain的Java大神之路2 天前
介绍一下分布式事务
java·分布式·后端·spring·spring cloud·架构·springcloud
大菠萝爱上小西瓜2 天前
【大数据实战】Spark 2.2.2 完全分布式集群搭建
大数据·分布式·spark
肠畔码农2 天前
深度解析 RocketMQ 消费端限流与重平衡(Rebalance):分布式队列分配与流控防雪崩本质
分布式·rocketmq