当前位置: 首页 > news >正文

石家庄网站建设电商电脑零基础培训学校

石家庄网站建设电商,电脑零基础培训学校,网站建设自,.net 网站开发实例SpringBoot第44讲:SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPXLua) Redis实际使用场景最为常用的还有通过Redis实现分布式锁。本文是SpringBoot第44讲,主要介绍Redis实现分布式锁 文章目录 SpringBoot第44讲:SpringBoot集成Re…

SpringBoot第44讲:SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPX+Lua)

Redis实际使用场景最为常用的还有通过Redis实现分布式锁。本文是SpringBoot第44讲,主要介绍Redis实现分布式锁

文章目录

  • SpringBoot第44讲:SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPX+Lua)
    • 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,当前时间+过期时间) + Lua
  • Redis集群模式:Redlock

在实现使用时,由于很多redis客户端包含了上述实现方式,我们可以通过redis客户端进行,更多可以看 分布式系统第四讲:分布式锁及实现方案

2、实现案例

本案例主要介绍 基于Jedis客户端下通过: setnx(key, 当前时间+过期时间) + Lua 实现分布式锁

2.1、定义Redis的分布式锁类

(具体看分布式系统 - 分布式锁及实现方案 中Redis实现分布式锁的部分)

加锁: set NX PX + 重试 + 重试间隔

向Redis发起如下命令: SET productId:lock 0xx9p03001 NX PX 30000 其中,"productId"由自己定义,可以是与本次业务有关的id,"0xx9p03001"是一串随机值,必须保证全局唯一(防止删除其他线程的锁,可以使用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 ThreadLocal<String> 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 {RedisCallback<String> 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 {List<String> keys = new ArrayList<>();keys.add(key);List<String> args = new ArrayList<>();args.add(lockFlag.get());// use lua scriptRedisCallback<Long> 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, what's 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

http://www.shuangfujiaoyu.com/news/57607.html

相关文章:

  • 做聚美优品网站得多少钱网络推广渠道排名
  • 做公众号封面的网站在线看seo网站
  • 广州天河区网站建设莆田百度快照优化
  • 怎么创建一个公司网站品牌搜索引擎服务优化
  • 佛山企业制作网站各大引擎搜索入口
  • 商城网站要怎样设计外链怎么做
  • 山西高端网站建设网站自动提交收录
  • 电商网站平台关键词采集软件
  • wordpress怎么添加企业网站微博推广方式有哪些
  • 做购物网站能赚钱吗百度网址导航主页
  • 购物网站策划建设方案无锡百度
  • 苏州做物流网站电话seo外包优化服务商
  • 英文网站怎么推广搜索关键词排名一般按照什么收费
  • 网站开发不提供源代码广州百度seo 网站推广
  • wordpress网站加载过慢最有效的线下推广方式
  • 湖南建网站网站如何宣传推广
  • 淄博易宝网站建设sem推广是什么意思呢
  • 网站建设需要考什么证长沙百度关键词排名
  • 如何查询网站备案信息2021年搜索引擎排名
  • 济宁企业做网站专业提升关键词排名工具
  • 网站如何防注册机百度指数怎么分析
  • erp软件前十名广西seo搜索引擎优化
  • 山东建设兵团网站广州百度推广客服电话多少
  • 网站建设添加背景命令东营网站建设费用
  • 自己做静态网站的步骤windows优化工具
  • 三联网站建设如何建造自己的网站
  • 定制网站系统开发杭州优化公司多少钱
  • 2018年做淘宝客网站还能挣钱吗网址大全浏览器主页
  • 遵化市有做奇麟网站的吗网站不收录怎么办
  • 做网站和做免费推广网站的区别线上推广平台