Redis分散ロック--Spring-boot-start-data-redisベース
目次
1 build.gradle導入JAR
2 プロファイル:bootstrap-dev.yml
3 RedisConfig.java
4 CacheService.java
現在Javaがredisを操作しているクライアントにはjedisとlettuceがあります.LettuceとJedisの位置付けはすべてRedisのclientなので、もちろんredis serverに直接接続することができます. Jedisは実装上直接接続されたredis serverであり、マルチスレッド環境で非スレッドが安全である場合、この場合は接続プールのみを使用してJedisインスタンスごとに物理接続 を追加するLettuceの接続はNettyに基づいており、接続インスタンス(StatefulRedisConnection)は複数のスレッド間で同時アクセス可能であり、StatefulRedisConnectionはスレッドが安全であるべきであるため、1つの接続インスタンス(StatefulRedisConnection)はマルチスレッド環境での同時アクセスを満たすことができ、もちろんこれも伸縮可能な設計であり、接続インスタンスが1つ足りない場合は、必要に応じて接続インスタンスを追加することもできます.
1 build.gradle導入JAR
lettuceの使用
spring-boot-start-data-redisを直接導入
jedisの使用
デフォルトではlettuceが使用され、jedisを使用する場合はexcludeを使用してlettuce-coreを除外し、jedisパッケージを導入できます.
注意:redis依存commons-poolこの依存は必ず追加します
2 プロファイル:bootstrap-dev.yml
lettuceの使用
jedisの使用
3 RedisConfig.java
redisのkeyとvalueのシーケンス化を構成する必要があります.デフォルトで使用されている
ここで、
4 CacheService.java
コードアドレス:https://github.com/tengxvincent/spring-boot-vincent.git
1 build.gradle導入JAR
2 プロファイル:bootstrap-dev.yml
3 RedisConfig.java
4 CacheService.java
現在Javaがredisを操作しているクライアントにはjedisとlettuceがあります.
springboot1.x
シリーズではjedis
が使用されているが、springboot2.x
になるとLettuce
が使用される.私たちのバージョンはspringboot2.x
シリーズなので、今日はLettuce
を使用しています.jedis
とlettuce
の違いについて:1 build.gradle導入JAR
lettuceの使用
compile "org.springframework.boot:spring-boot-starter-data-redis"
implementation 'org.apache.commons:commons-pool2'
spring-boot-start-data-redisを直接導入
jedisの使用
compile("org.springframework.boot:spring-boot-starter-data-redis"){
exclude module: 'lettuce-core'
}
compile 'redis.clients:jedis:2.9.0'
デフォルトではlettuceが使用され、jedisを使用する場合はexcludeを使用してlettuce-coreを除外し、jedisパッケージを導入できます.
注意:redis依存commons-poolこの依存は必ず追加します
2 プロファイル:bootstrap-dev.yml
lettuceの使用
server:
port: 8989
spring:
redis:
host: 127.0.0.1
port: 6379
#
password: 123456
# jedis lettuce jedis
lettuce:
pool:
# 8
max-active: 8
# 8
max-idle: 8
# 0
min-idle: 0
jedisの使用
redis:
database: 0
host: 127.0.0.1
port: 30038
password:
timeout: 60000ms
jedis:
pool:
# ( )
max-wait: -1ms
# ( )
max-active: -1
3 RedisConfig.java
redisのkeyとvalueのシーケンス化を構成する必要があります.デフォルトで使用されている
JdkSerializationRedisSerializer
は、redis desktop manager
で表示されている私たちのkeyとvalueを通過するときに正常な文字ではないことを表示します.したがって、シーケンス化方式を手動で構成して、config
パッケージを新規作成し、その次にRedisConfig.java
の具体的なコードを新規作成する必要があります.@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
// Jackson2JsonRedisSerializer redis value
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key String
template.setKeySerializer(stringRedisSerializer);
// hash key String
template.setHashKeySerializer(stringRedisSerializer);
// value jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash value jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
ここで、
@Configuration
はこのクラスが構成クラスであることを表し、@AutoConfigureAfter(RedisAutoConfiguration.class)
は、この構成クラスを内蔵された構成クラス(RedisAutoConfiguration.class)の後に構成することで、構成クラスが有効になり、構成が上書きされないことを保証します.メソッド名は必ずredisTemplate
と呼ぶ必要があります.なぜなら@Bean
注釈はメソッド名に基づいてこのbeanのnameを構成しているからです.4 CacheService.java
@Service
public class CacheService {
private static Logger logger = LoggerFactory.getLogger(CacheService.class);
private static final Long SUCCESS = 1L;
@Autowired
private RedisTemplate redisTemplate;
/**
*
*
* @param key
* @param time ( )
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
}
/**
* key
*
* @param key null
* @return ( ) 0
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* key
*
* @param key
* @return true false
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
}
/**
*
*
* @param key
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/**
*
*
* @param key
* @return
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
*
*
* @param key
* @param value
* @return true false
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
}
/**
*
*
* @param key
* @param value
* @param time ( ) time 0 time 0
* @return true false
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
logger.error(e.getMessage(), e);
return false;
}
}
/**
*
*
* @param key
* @param delta ( 0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException(" 0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
*
*
* @param key
* @param delta ( 0)
* @return 147
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException(" 0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
/**
*
* @param lockKey
* @param requestId
* @param expireTime
* @param waitTimeout
* @return
*/
public boolean tryLock(String lockKey, String requestId, int expireTime,long waitTimeout) {
long nanoTime = System.nanoTime(); //
try{
String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
logger.info(" -key[{}]",lockKey);
int count = 0;
do{
RedisScript redisScript = new DefaultRedisScript<>(script, Long.class);
logger.debug(" -key[{}]requestId[{}]count[{}]",lockKey,requestId,count);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),requestId,expireTime);
if(SUCCESS.equals(result)) {
logger.debug(" -key[{}] ",lockKey);
return true;
}
Thread.sleep(500L);// 500
count++;
}while ((System.nanoTime() - nanoTime) < TimeUnit.MILLISECONDS.toNanos(waitTimeout));
}catch(Exception e){
logger.error(" -key[{}] ",lockKey);
logger.error(e.getMessage(),e);
}
return false;
}
/**
*
* @param lockKey
* @param requestId
* @return
*/
public boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript redisScript = new DefaultRedisScript<>(script, Long.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
if (SUCCESS.equals(result)) {
return true;
}
return false;
}
}
コードアドレス:https://github.com/tengxvincent/spring-boot-vincent.git