网站建设自查情况,手机上page转换wordpress,花都网站建设策划,凡客诚品官网的关闭原因SpringBoot第44讲#xff1a;SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPXLua) Redis实际使用场景最为常用的还有通过Redis实现分布式锁。本文是SpringBoot第44讲#xff0c;主要介绍Redis实现分布式锁 文章目录 SpringBoot第44讲#xff1a;SpringBoot集成Re…SpringBoot第44讲SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPXLua) Redis实际使用场景最为常用的还有通过Redis实现分布式锁。本文是SpringBoot第44讲主要介绍Redis实现分布式锁 文章目录 SpringBoot第44讲SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPXLua)1、知识准备1.1、什么是分布式锁分布式锁有哪些实现方式1.2、Redis的分布式锁有哪些实现方式 2、实现案例2.1、定义Redis的分布式锁类2.2、定义AOP拦截点2.3、定义AOP切面2.4、切面使用 3、示例源码 1、知识准备 需要了解为何要用分布式锁以及分布式锁常见的实现方式以及如何通过Redis实现分布式锁的几种方式。 1.1、什么是分布式锁分布式锁有哪些实现方式
分布式锁相关的内容请参考 分布式系统第四讲分布式锁及实现方案
1.2、Redis的分布式锁有哪些实现方式 主要有两种思路 单个Redis实例setnx(key,当前时间过期时间) LuaRedis集群模式Redlock
在实现使用时由于很多redis客户端包含了上述实现方式我们可以通过redis客户端进行更多可以看 分布式系统第四讲分布式锁及实现方案
2、实现案例 本案例主要介绍 基于Jedis客户端下通过 setnx(key, 当前时间过期时间) Lua 实现分布式锁 2.1、定义Redis的分布式锁类 具体看分布式系统 - 分布式锁及实现方案 中Redis实现分布式锁的部分 加锁 set NX PX 重试 重试间隔
向Redis发起如下命令: SET productId:lock 0xx9p03001 NX PX 30000 其中productId由自己定义可以是与本次业务有关的id0xx9p03001是一串随机值必须保证全局唯一(防止删除其他线程的锁可以使用traceid作为value)“NX指的是当且仅当key(也就是案例中的productId:lock”)在Redis中不存在时返回执行成功否则执行失败。PX 30000指的是在30秒后key将被自动删除。执行命令后返回成功表明服务成功的获得了锁。
解锁采用lua脚本
在删除key之前一定要判断服务A持有的value与Redis内存储的value是否一致。如果贸然使用服务A持有的key来删除锁则会误将服务B的锁释放掉。
if redis.call(get, KEYS[1])ARGV[1] thenreturn redis.call(del, KEYS[1])
elsereturn 0
end具体的封装类 RedisDistributedLock 如下
package springboot.redis.jedis.lock.lock;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.commands.JedisCommands;
import redis.clients.jedis.params.SetParams;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;/*** author qiwenjie*/
Slf4j
public class RedisDistributedLock {/*** lua script for unlock.*/private static final String UNLOCK_LUA;static {StringBuilder sb new StringBuilder();sb.append(if redis.call(\get\,KEYS[1]) ARGV[1] );sb.append(then );sb.append( return redis.call(\del\,KEYS[1]) );sb.append(else );sb.append( return 0 );sb.append(end );UNLOCK_LUA sb.toString();}/*** unique lock flag based on thread local.*/private final ThreadLocalString lockFlag new ThreadLocal();private final StringRedisTemplate redisTemplate;public RedisDistributedLock(StringRedisTemplate redisTemplate) {this.redisTemplate redisTemplate;}public boolean lock(String key, long expire, int retryTimes, long retryDuration) {// use JedisCommands instead of setIfAbsenseboolean result setRedis(key, expire);// retry if neededwhile ((!result) retryTimes-- 0) {try {log.debug(lock failed, retrying... retryTimes);Thread.sleep(retryDuration);} catch (Exception e) {return false;}// use JedisCommands instead of setIfAbsenseresult setRedis(key, expire);}return result;}private boolean setRedis(String key, long expire) {try {RedisCallbackString redisCallback connection - {JedisCommands commands (JedisCommands) connection.getNativeConnection();String uuid UUID.randomUUID().toString(); // change to distribute UUID generation.lockFlag.set(uuid);return commands.set(key, uuid, SetParams.setParams().nx().px(expire));};String result redisTemplate.execute(redisCallback);return !StringUtils.isEmpty(result);} catch (Exception e) {log.error(set redis occurred an exception, e);}return false;}public boolean unlock(String key) {boolean success false;try {ListString keys new ArrayList();keys.add(key);ListString args new ArrayList();args.add(lockFlag.get());// use lua scriptRedisCallbackLong redisCallback connection - {Object nativeConnection connection.getNativeConnection();if (nativeConnection instanceof JedisCluster) { // cluster modereturn (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);} else if (nativeConnection instanceof Jedis) { // single modereturn (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);}return 0L;};Long result redisTemplate.execute(redisCallback);success result ! null result 0;} catch (Exception e) {log.error(release lock occurred an exception, e);} finally {if (success) {lockFlag.remove();}}return success;}
}2.2、定义AOP拦截点
定义RedisLock注解
package springboot.redis.jedis.lock.annotation;import java.lang.annotation.*;/*** author qiwenjie*/
Target({ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Inherited
public interface RedisLock {/*** redis lock key as value.** return lock key*/String value() default ;/*** how long we hold the lock.** return mills*/long expireMills() default 30000;/*** if lock failed, do we need to retry, default retry 0 means NO retry.** return retry times*/int retryTimes() default 0;/*** when we retry to get lock, whats the duration for next retry.** return mills*/long retryDurationMills() default 200;
}2.3、定义AOP切面
定义AOP切面类RedisLockAspect用来拦截RedisLock注解方法并调用RedisDistributedLock对方法加锁处理。
package springboot.redis.jedis.lock.lock;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import springboot.redis.jedis.lock.annotation.RedisLock;import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;/*** author qiwenjie*/
Slf4j
Aspect
Configuration
public class RedisLockAspect {/*** lock impl.*/Resourceprivate RedisDistributedLock distributedLock;/*** AOP, around PJP.** param pjp ProceedingJoinPoint* return Object* throws Throwable Throwable*/Around(annotation(springboot.redis.jedis.lock.annotation.RedisLock))public Object around(ProceedingJoinPoint pjp) throws Throwable {// get attribute through annotationMethod method ((MethodSignature) pjp.getSignature()).getMethod();RedisLock redisLock method.getAnnotation(RedisLock.class);String key redisLock.value();if (StringUtils.isEmpty(key)) {Object[] args pjp.getArgs();key Arrays.toString(args);}// do lockboolean lock distributedLock.lock(key, redisLock.expireMills(), redisLock.retryTimes(),redisLock.retryDurationMills());if (!lock) {// 加锁失败不执行业务逻辑log.debug(get lock failed, key: {}, key);return null;}// execute method, and unlocklog.debug(get lock success, key: {}, key);try {// executereturn pjp.proceed();} catch (Exception e) {log.error(execute locked method occurred an exception, e);} finally {// unlockboolean releaseResult distributedLock.unlock(key);log.debug(release lock: {}, success: {}, key, releaseResult);}return null;}
}2.4、切面使用
只需要在对应方法添加RedisLock注解即可实现分布式锁
RedisLock
public void xxxMethod() {}分布式锁在项目中的使用可以参考这篇文章
项目实战第四十二讲分布式环境下使用ResubmitCheck注解进行防重校验
3、示例源码
todo