本篇文章給大家主要帶大家了解一下redis中分布式鎖的實(shí)現(xiàn)和代碼解析,希望對大家有所幫助!
redis 分布式鎖
大家項(xiàng)目中都會(huì)使用到分布式鎖把,通常用來做數(shù)據(jù)的有序操作場景,比如一筆訂單退款(如果可以退多次的情況)。或者用戶多端下單。【相關(guān)推薦:Redis視頻教程】
Maven 依賴
我主要是基于 Spring-Boot 2.1.2 + Jedis 進(jìn)行實(shí)現(xiàn)
<?xml version="1.0" encoding="UTF-8"?><project> ????<modelversion>4.0.0</modelversion> ????<parent> ????????<groupid>org.springframework.boot</groupid> ????????<artifactid>spring-boot-starter-parent</artifactid> ????????<version>2.1.2.RELEASE</version> ????</parent> ????<groupid>cn.edu.cqvie</groupid> ????<artifactid>redis-lock</artifactid> ????<version>1.0-SNAPSHOT</version> ????<properties> ????????<project.build.sourceencoding>UTF-8</project.build.sourceencoding> ????????<java.version>1.8</java.version> ????????<redis.version>2.9.0</redis.version> ????????<spring-test.version>5.0.7</spring-test.version> ????</properties> ????<dependencies> ????????<dependency> ????????????<groupid>org.springframework.boot</groupid> ????????????<artifactid>spring-boot-autoconfigure</artifactid> ????????</dependency> ????????<dependency> ????????????<groupid>org.springframework.data</groupid> ????????????<artifactid>spring-data-redis</artifactid> ????????</dependency> ????????<dependency> ????????????<groupid>redis.clients</groupid> ????????????<artifactid>jedis</artifactid> ????????????<version>${redis.version}</version> ????????</dependency> ????????<dependency> ????????????<groupid>org.springframework.boot</groupid> ????????????<artifactid>spring-boot-starter-logging</artifactid> ????????</dependency> ????????<dependency> ????????????<groupid>org.slf4j</groupid> ????????????<artifactid>log4j-over-slf4j</artifactid> ????????</dependency> ????????<dependency> ????????????<groupid>org.springframework.boot</groupid> ????????????<artifactid>spring-boot-starter-test</artifactid> ????????????<scope>test</scope> ????????</dependency> ????</dependencies> ????<build> ????????<plugins> ????????????<plugin> ????????????????<groupid>org.springframework.boot</groupid> ????????????????<artifactid>spring-boot-maven-plugin</artifactid> ????????????</plugin> ????????</plugins> ????</build></project>
配置文件
application.properties 配置文件內(nèi)容如下:
spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= spring.redis.timeout=30000 spring.redis.jedis.pool.max-active=8 spring.redis.jedis.pool.min-idle=2 spring.redis.jedis.pool.max-idle=4 logging.level.root=INFO
接口定義
接口定義,對于鎖我們核心其實(shí)就連個(gè)方法 lock 和 unlock.
public?interface?RedisLock?{ ????long?TIMEOUT_MILLIS?=?30000; ????int?RETRY_MILLIS?=?30000; ????long?SLEEP_MILLIS?=?10; ????boolean?tryLock(String?key); ????boolean?lock(String?key); ????boolean?lock(String?key,?long?expire); ????boolean?lock(String?key,?long?expire,?long?retryTimes); ????boolean?unlock(String?key); }
分布式鎖實(shí)現(xiàn)
我的實(shí)現(xiàn)方式是通過 setnx 方式實(shí)現(xiàn)了,如果存在 tryLock 邏輯的話,會(huì)通過 自旋 的方式重試
//?AbstractRedisLock.java?抽象類 public?abstract?class?AbstractRedisLock?implements?RedisLock?{ ????@Override ????public?boolean?lock(String?key)?{ ????????return?lock(key,?TIMEOUT_MILLIS); ????} ????@Override ????public?boolean?lock(String?key,?long?expire)?{ ????????return?lock(key,?TIMEOUT_MILLIS,?RETRY_MILLIS); ????} } //?具體實(shí)現(xiàn) @Component public?class?RedisLockImpl?extends?AbstractRedisLock?{ ????private?Logger?logger?=?LoggerFactory.getLogger(getClass()); ????@Autowired ????private?RedisTemplate<string>?redisTemplate; ????private?ThreadLocal<string>?threadLocal?=?new?ThreadLocal<string>(); ????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(); ????} ????@Override ????public?boolean?tryLock(String?key)?{ ????????return?tryLock(key,?TIMEOUT_MILLIS); ????} ????public?boolean?tryLock(String?key,?long?expire)?{ ????????try?{ ????????????return?!StringUtils.isEmpty(redisTemplate.execute((RedisCallback<string>)?connection?->?{ ????????????????JedisCommands?commands?=?(JedisCommands)?connection.getNativeConnection(); ????????????????String?uuid?=?UUID.randomUUID().toString(); ????????????????threadLocal.set(uuid); ????????????????return?commands.set(key,?uuid,?"NX",?"PX",?expire); ????????????})); ????????}?catch?(Throwable?e)?{ ????????????logger.error("set?redis?occurred?an?exception",?e); ????????} ????????return?false; ????} ????@Override ????public?boolean?lock(String?key,?long?expire,?long?retryTimes)?{ ????????boolean?result?=?tryLock(key,?expire); ????????while?(!result?&&?retryTimes--?>?0)?{ ????????????try?{ ????????????????logger.debug("lock?failed,?retrying...{}",?retryTimes); ????????????????Thread.sleep(SLEEP_MILLIS); ????????????}?catch?(InterruptedException?e)?{ ????????????????return?false; ????????????} ????????????result?=?tryLock(key,?expire); ????????} ????????return?result; ????} ????@Override ????public?boolean?unlock(String?key)?{ ????????try?{ ????????????List<string>?keys?=?Collections.singletonList(key); ????????????List<string>?args?=?Collections.singletonList(threadLocal.get()); ????????????Long?result?=?redisTemplate.execute((RedisCallback<long>)?connection?->?{ ????????????????Object?nativeConnection?=?connection.getNativeConnection(); ????????????????if?(nativeConnection?instanceof?JedisCluster)?{ ????????????????????return?(Long)?((JedisCluster)?nativeConnection).eval(UNLOCK_LUA,?keys,?args); ????????????????} ????????????????if?(nativeConnection?instanceof?Jedis)?{ ????????????????????return?(Long)?((Jedis)?nativeConnection).eval(UNLOCK_LUA,?keys,?args); ????????????????} ????????????????return?0L; ????????????}); ????????????return?result?!=?null?&&?result?>?0; ????????}?catch?(Throwable?e)?{ ????????????logger.error("unlock?occurred?an?exception",?e); ????????} ????????return?false; ????} }</long></string></string></string></string></string></string>
測試代碼
最后再來看看如何使用吧. (下面是一個(gè)模擬秒殺的場景)
@RunWith(SpringRunner.class) @SpringBootTest public?class?RedisLockImplTest?{ ????private?Logger?logger?=?LoggerFactory.getLogger(getClass()); ????@Autowired ????private?RedisLock?redisLock; ????@Autowired ????private?StringRedisTemplate?redisTemplate; ????private?ExecutorService?executors?=?Executors.newScheduledThreadPool(8); ????@Test ????public?void?lock()?{ ????????//?初始化庫存 ????????redisTemplate.opsForValue().set("goods-seckill",?"10"); ????????List<future>?futureList?=?new?ArrayList(); ????????for?(int?i?=?0;?i??{ ????????????try?{ ????????????????action.get(); ????????????}?catch?(InterruptedException?|?ExecutionException?e)?{ ????????????????e.printStackTrace(); ????????????} ????????}); ????} ????public?int?seckill()?{ ????????String?key?=?"goods"; ????????try?{ ????????????redisLock.lock(key); ????????????int?num?=?Integer.valueOf(Objects.requireNonNull(redisTemplate.opsForValue().get("goods-seckill"))); ????????????if?(num?>?0)?{ ????????????????redisTemplate.opsForValue().set("goods-seckill",?String.valueOf(--num)); ????????????????logger.info("秒殺成功,剩余庫存:{}",?num); ????????????}?else?{ ????????????????logger.error("秒殺失敗,剩余庫存:{}",?num); ????????????} ????????????return?num; ????????}?catch?(Throwable?e)?{ ????????????logger.error("seckill?exception",?e); ????????}?finally?{ ????????????redisLock.unlock(key); ????????} ????????return?0; ????} }</future>
總結(jié)
本文是 Redis 鎖的一種簡單的實(shí)現(xiàn)方式,基于 jedis 實(shí)現(xiàn)了鎖的重試操作。 但是缺點(diǎn)還是有的,不支持鎖的自動(dòng)續(xù)期,鎖的重入,以及公平性(目前通過自旋的方式實(shí)現(xiàn),相當(dāng)于是非公平的方式)。
更多編程相關(guān)知識,請?jiān)L問:Redis視頻教程!!
? 版權(quán)聲明
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載。
THE END