<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> <version>2.1.6.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
spring-boot-starter-data-redis:
在低版本中默認依賴 Jedis
在高版本中默認依賴 lettuce,若要使用Jedis 則須要修改依賴(如上所示)java
application.properties 或者 application.yml 均可以(只是有格式的區別,做用並沒有差異)redis
spring.redis.host=127.0.0.1 spring.redis.database=0 spring.redis.port=6379 spring.redis.jedis.pool.max-active=8 spring.redis.jedis.pool.max-wait=-1 spring.redis.jedis.pool.max-idle=8 spring.redis.jedis.pool.min-idle=0 spring.redis.password=redis123456
package com.uzhizhe.user.cache; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisCallback; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import java.util.Collections; /** * @Desc Redis service * @Author uzhizhe */ @Service @Slf4j public class RedisService { /** * 操做成功 */ private static final Long RELEASE_SUCCESS = 1L; /** * 操做成功 */ private static final String LOCK_SUCCESS = "OK"; /** * 不存在才set */ private static final String SET_IF_NOT_EXIST = "NX"; /** * 毫秒單位 */ private static final String SET_WITH_EXPIRE_TIME = "PX"; /** * 釋放分佈式鎖的Lua 腳本 */ private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; /** * 默認分佈式鎖過時時間 */ private static final Integer DEFALUT_EXPIRE_TIME = 5000; /** * redisTemplate */ @Autowired private StringRedisTemplate redisTemplate; /** * 獲取分佈式鎖 * * @param lockKey 加鎖鍵 * @param requestId 加鎖客戶端惟一標識(採用UUID) * @param expireTime 鎖過時時間, 單位:毫秒 * @return 獲取鎖成功或者失敗 */ public boolean tryLock(String lockKey, String requestId, long expireTime) { return redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> { Jedis jedis = (Jedis) redisConnection.getNativeConnection(); String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); return LOCK_SUCCESS.equals(result); }); } /** * 釋放分佈式鎖 * * @param lockKey lockKey * @param requestId requestId 加鎖時的惟一ID * @return 釋放鎖成功或者失敗 */ public boolean releaseLock(String lockKey, String requestId) { return redisTemplate.execute((RedisCallback<Boolean>) redisConnection -> { Jedis jedis = (Jedis) redisConnection.getNativeConnection(); Object result = jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(lockKey), Collections.singletonList(requestId)); return RELEASE_SUCCESS.equals(result); }); } }
利用redis 的set方法實現加鎖
1. set 的5個參數,可以很好的保證加鎖的正確性
2. 過時時間:保證不會出現死鎖,在沒有主動釋放時,會因過時釋放
3. 設置Value值,保證本身的鎖不會被別人釋放(所謂:解鈴還須繫鈴人)
4. Redis 命令的原子性,保證了鎖的互斥性spring