Redis实现分布式锁

redis实现,使用setnx+value+expireTime

maven依赖

<!-- redis实现分布式锁,maven仓库用户最多 -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
<!-- 注解开发 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<!-- hutool jar SpringUtil工具类使用 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.7</version>
</dependency>

Java接口定义

package com.hncu.ex.common.base.service;

/**
 * 分布式锁定义
 */
public interface IDistributeLockService {

    /**
     * 获取锁
     *
     * @param lockKey    锁
     * @param lockValue  请求对象
     * @param expireTime 过期时间
     * @return 是否获取锁
     */
    boolean lock(String lockKey, String lockValue, int expireTime);

    /**
     * 释放锁
     *
     * @param lockKey   锁
     * @param lockValue 请求对象
     * @return 是否释放锁
     */
    boolean unlock(String lockKey, String lockValue);

}

接口实现

package com.hncu.ex.common.base.service.impl;

import com.hncu.ex.common.base.service.IDistributeLockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.util.Collections;

/**
 * redis实现分布式锁
 */
@Slf4j
@Component("redisDistributeLockService")
public class RedisDistributeLockService implements IDistributeLockService {
    /**
     * 设置成功标识
     */
    private static final String SET_SUCCESS = "OK";
    /**
     * NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。
     * EX seconds :设定key的过期时间,时间单位是秒。
     * PX milliseconds: 设定key的过期时间,单位为毫秒
     * XX: 仅当key存在时设置值
     */
    private static final String LUA_SCRIPTS = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
    /**
     * redis对象
     */
    private Jedis jedis;

    public RedisDistributeLockService() {
        init();
    }

    // 初始化加载
    private void init() {
        //1. 获取连接
        jedis = new Jedis("localhost", 6379);
        log.info("redis初始化成功。。。");
    }

    /**
     * 获取锁
     *
     * @param lockKey    锁
     * @param lockValue  请求对象
     * @param expireTime 过期时间,单位:毫秒
     * @return 是否获取锁
     */
    @Override
    public boolean lock(String lockKey, String lockValue, int expireTime) {
        log.info("获取锁,lockKey-[{}],lockValue-[{}],毫秒-[{}]", lockKey, lockValue, expireTime);
        String msg = jedis.set(lockKey, lockValue, "NX", "PX", expireTime);
        //判断是否成功
        return SET_SUCCESS.equals(msg);
    }

    /**
     * 释放锁
     *
     * @param lockKey   锁
     * @param lockValue 请求对象
     * @return 是否释放锁
     */
    @Override
    public boolean unlock(String lockKey, String lockValue) {
        log.info("释放锁,lockKey-[{}],lockValue-[{}]", lockKey, lockValue);
        // 调用lua脚本,执行setnx命令
        Object result = jedis.eval(LUA_SCRIPTS, Collections.singletonList(lockKey), Collections.singletonList(lockValue));
        //判断是否成功
        return result.equals(1L);
    }
}

工具类

package com.hncu.ex.common.base.util;

import cn.hutool.extra.spring.SpringUtil;
import com.hncu.ex.common.base.service.impl.RedisDistributeLockService;

/**
 * 分布式锁工具类
 */
public class DistributeLockUtils {
    /**
     * key前缀
     */
    public static final String REDIS_KEY_PREFIX = "hncu.lock.redis.";
    /**
     * key过期时间,3秒=3*1000
     */
    public static final int REDIS_KEY_EXPIRE_TIME = 3000;

    /**
     * 获取锁
     *
     * @param lockKey   锁
     * @param lockValue 请求对象
     * @return 是否获取锁
     */
    public static boolean lock(String lockKey, String lockValue) {
        String key = REDIS_KEY_PREFIX + lockKey;
        RedisDistributeLockService lockService = SpringUtil.getBean(RedisDistributeLockService.class);
        return lockService.lock(key, lockValue, REDIS_KEY_EXPIRE_TIME);
    }

    /**
     * 释放锁
     *
     * @param lockKey   锁
     * @param lockValue 请求对象
     * @return 是否释放锁
     */
    public static boolean unlock(String lockKey, String lockValue) {
        String key = REDIS_KEY_PREFIX + lockKey;
        RedisDistributeLockService lockService = SpringUtil.getBean(RedisDistributeLockService.class);
        return lockService.unlock(key, lockValue);
    }

}

业务调用案例

package com.hncu.ex.common.base.service.impl;

import com.hncu.ex.common.base.service.IBusinessService;
import com.hncu.ex.common.base.util.DistributeLockUtils;
import com.hncu.ex.common.dto.common.base.InjectTaskReq;
import com.hncu.ex.common.dto.common.base.InjectTaskRsp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Slf4j
@Service
public class BusinessServiceImpl implements IBusinessService {

    @Override
    public InjectTaskRsp injectTask(InjectTaskReq req) {
        InjectTaskRsp result = new InjectTaskRsp();
        String taskId = req.getTaskId();
        if (StringUtils.isEmpty(taskId)) {
            result.setMsg("taskId is not empty");
            return result;
        }

        String lockKey = taskId;
        String lockValue = UUID.randomUUID().toString().replaceAll("-", "");
        boolean lock = DistributeLockUtils.lock(lockKey, lockValue);
        String msg = "失败";

        try {
            if (lock) {
                msg = "锁获取成功,注入任务成功,该接口需要运行3秒,期间不可访问-" + lockValue;
                log.info(msg);
                Thread.sleep(3000);
            } else {
                msg = "获取分布式锁失败-" + lockValue;
                log.info(msg);
            }
        } catch (Exception e) {
            log.error("注入任务失败-" + lockValue, e);
        } finally {
            if (lock) {
                DistributeLockUtils.unlock(lockKey, lockValue);
            }
        }

        result.setMsg(msg);
        return result;
    }
}

postMan调用

http://localhost:6666/common/api/inject/injectTask
{
    "taskId": "202303070001"
}

日志打印

2023-03-07 20:20:16.842 INFO  [http-nio-6666-exec-2] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[58339998d0c0405c8a3210b6cd05a6d4],毫秒-[3000]
2023-03-07 20:20:16.847 INFO  [http-nio-6666-exec-2] c.h.e.common.base.service.impl.BusinessServiceImpl:34 - 锁获取成功,注入任务成功,该接口需要运行3秒,期间不可访问-58339998d0c0405c8a3210b6cd05a6d4
2023-03-07 20:20:17.812 INFO  [http-nio-6666-exec-4] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[c91acf4299094676a62f437f0c85e31f],毫秒-[3000]
2023-03-07 20:20:17.812 INFO  [http-nio-6666-exec-4] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-c91acf4299094676a62f437f0c85e31f
2023-03-07 20:20:18.189 INFO  [http-nio-6666-exec-10] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[d5102e3063a94def8f7603b1b4f92e5f],毫秒-[3000]
2023-03-07 20:20:18.189 INFO  [http-nio-6666-exec-10] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-d5102e3063a94def8f7603b1b4f92e5f
2023-03-07 20:20:18.724 INFO  [http-nio-6666-exec-3] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[043bb1466a7048da9e560a6981799c08],毫秒-[3000]
2023-03-07 20:20:18.724 INFO  [http-nio-6666-exec-3] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-043bb1466a7048da9e560a6981799c08
2023-03-07 20:20:19.266 INFO  [http-nio-6666-exec-7] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[5d659ebab64b40f1bb911ccd5abc0596],毫秒-[3000]
2023-03-07 20:20:19.268 INFO  [http-nio-6666-exec-7] c.h.e.common.base.service.impl.BusinessServiceImpl:38 - 获取分布式锁失败-5d659ebab64b40f1bb911ccd5abc0596
2023-03-07 20:20:19.847 INFO  [http-nio-6666-exec-2] c.h.e.c.b.service.impl.RedisDistributeLockService:68 - 释放锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[58339998d0c0405c8a3210b6cd05a6d4]
2023-03-07 20:20:19.848 INFO  [http-nio-6666-exec-8] c.h.e.c.b.service.impl.RedisDistributeLockService:53 - 获取锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[de03d1ad87ca444291c1f9162825ce90],毫秒-[3000]
2023-03-07 20:20:19.848 INFO  [http-nio-6666-exec-8] c.h.e.common.base.service.impl.BusinessServiceImpl:34 - 锁获取成功,注入任务成功,该接口需要运行3秒,期间不可访问-de03d1ad87ca444291c1f9162825ce90
2023-03-07 20:20:22.849 INFO  [http-nio-6666-exec-8] c.h.e.c.b.service.impl.RedisDistributeLockService:68 - 释放锁,lockKey-[hncu.lock.redis.202303070001],lockValue-[de03d1ad87ca444291c1f9162825ce90]

启动类

package com.hncu.ex.common;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

// mybatisPlus需要加载mapper接口
@MapperScan(value = {"com.hncu.ex.**.dao", "com.hncu.ex.**.mapper"})
// 使用HuTool工具包中的SpringUtil
@SpringBootApplication(scanBasePackages = {"com.hncu.ex", "cn.hutool.extra.spring"})
public class CommonWebApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(CommonWebApplication.class, args);
        System.out.println("服务启动,接口为 " + run.getEnvironment().getProperty("server.port"));
    }

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,
     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }

}

日志配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60000">
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{50}:%L - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 自定义日志级别 -->
    <logger name="org.springframework.web" level="INFO"/>

    <!-- 日志输出级别 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

公共返回类

package com.hncu.ex.common.base;

import lombok.Data;

/**
 * 接口返回类
 */
@Data
public class EduResult<T> {
    public static final String SUCCESS = "200";
    public static final String SUCCESS_MSG = "success";
    public static final String FAIL = "401";
    public static final String FAIL_MSG = "fail";

    private String code;
    private String msg;
    private T data;

    public EduResult() {
        this(SUCCESS, SUCCESS_MSG, null);
    }

    public EduResult(String code, String msg) {
        this(code, msg, null);
    }

    public EduResult(String code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static <T> EduResult build(String code, String msg, T data) {
        return new EduResult(code, msg, data);
    }

    public static <T> EduResult success(String code, String msg, T data) {
        return new EduResult(code, msg, data);
    }

    public static <T> EduResult success(String msg, T data) {
        return new EduResult(SUCCESS, msg, data);
    }

    public static EduResult success(String msg) {
        return new EduResult(SUCCESS, msg, null);
    }

    public static <T> EduResult success(T data) {
        return new EduResult(SUCCESS, SUCCESS_MSG, data);
    }

    public static EduResult success() {
        return new EduResult(SUCCESS, SUCCESS_MSG, null);
    }

    public static <T> EduResult fail(String code, String msg, T data) {
        return new EduResult(code, msg, data);
    }

    public static <T> EduResult fail(String msg, T data) {
        return new EduResult(FAIL, msg, data);
    }

    public static EduResult fail(String msg) {
        return new EduResult(FAIL, msg, null);
    }

    public static <T> EduResult fail(T data) {
        return new EduResult(FAIL, FAIL_MSG, data);
    }

    public static EduResult fail() {
        return new EduResult(FAIL, FAIL_MSG, null);
    }

}

请求对象

package com.hncu.ex.common.dto.common.base;

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**
 * <p>
 * 注入任务表
 * </p>
 *
 * @author LiuQiang
 * @since 2023-03-07
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class InjectTaskReq implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 任务主键
     */
    private String taskId;

    /**
     * 任务主题
     */
    private String taskTile;

    /**
     * 任务内容
     */
    private String taskContent;


}

返回对象

package com.hncu.ex.common.dto.common.base;

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;

/**
 * <p>
 * 注入任务表
 * </p>
 *
 * @author LiuQiang
 * @since 2023-03-07
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class InjectTaskRsp implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 任务消息
     */
    private String msg;


}

代码仓库地址

https://gitee.com/jadenJunLanLiu/study3.git

在common-web工程中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值