老張開發 springboot下Redistemplate實現併發鎖
阿新 • • 發佈:2018-12-24
謹以此記錄學習redis併發鎖學習筆記:
基於傳統的單機模式下的併發鎖,已遠遠不能滿足當下高併發大負載的情況,當下常用的併發處理如下
1、使用synchronized關鍵字
2、select for update 樂觀鎖
3、使用redis實現同步鎖
方案一 適合單機模式,
方案二 雖然滿足多節點服務例項但 對變更操作的吞吐量有影響
方案三 基於redis nosql資料庫 在效率與橫向擴充套件方面都大大優於前兩種方案
redis 單執行緒 在自身設計上一定程度可避免想成不安全 再者其效率高於關係型資料庫
本次實現鎖機制 基於redis 的兩個指令 查詢 redis.cn 網站
指令一:SETNX key value
將key
設定值為value
,如果key
不存在,這種情況下等同SET命令。 當key
存在時,什麼也不做。SETNX
是”SET if Not eX
返回值
Integer reply, 特定值:
1
如果key被設定了0
如果key沒有被設定
例子
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
redis>
指令二:GETSET key value
自動將key對應到value並且返回原來key對應的value。如果key存在但是對應的value不是字串,就返回錯誤。
返回值
bulk-string-reply: 返回之前的舊值,如果之前Key
不存在將返回nil
。
例子
redis> INCR mycounter
(integer) 1
redis> GETSET mycounter "0"
"1"
redis> GET mycounter
"0"
redis>
步驟一:引入依賴
<!-- 配置redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
步驟二: 配置redis連線
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(lettuceConnectionFactory);
return template;
}
@Bean
@Override
public CacheErrorHandler errorHandler() {
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
log.error("redis異常:key=[{}]", key, exception);
}
@Override
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
log.error("redis異常:key=[{}]", key, exception);
}
@Override
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
log.error("redis異常:key=[{}]", key, exception);
}
@Override
public void handleCacheClearError(RuntimeException exception, Cache cache) {
log.error("redis異常:", exception);
}
};
}
}
程式碼demo:將Redistemplate 注入需要使用的地方
public class RedisLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 加鎖
* @param key 鍵
* @param value 當前時間 + 超時時間
* @return 是否拿到鎖
*/
public boolean lock(String key, String value) {
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = redisTemplate.opsForValue().get(key);
//如果鎖過期
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
//是否已被別人搶佔
return StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue);
}
return false;
}
/**
* 解鎖
*
* @param key 鍵
* @param value 當前時間 + 超時時間
*/
public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("redis解鎖異常");
}
}
}
使用demo:
private void getJsapiTicketCatch(String appid, Map<String, String> resultMap) {
long expr = 90 * 60 * 1000L;
long ex = 5 * 1000L;
String value = String.valueOf(System.currentTimeMillis() + ex);
boolean lock = redisLock.lock(appid, value);
//獲取鎖
if (lock) {
String tick = redisTemplate.opsForValue().get("zqs"+appid);
if (StringUtils.isNotBlank(tick)) {
resultMap.put("tick",tick);
} else {
//token 通過appid
BigAuthorizationInfo authorInfoByAppidService = authorizedService.getAuthorInfoByAppidService(appid);
if (authorInfoByAppidService == null
|| StringUtils.isBlank(authorInfoByAppidService.getAuthorizer_access_token())) {
resultMap.put("tick",null);
}else {
String token = authorInfoByAppidService.getAuthorizer_access_token();
String jsapiTicket = WeShareUtils.GetJsapiTicket(token);
if (StringUtils.isBlank(jsapiTicket)) {
resultMap.put("tick",null);
}else {
resultMap.put("tick",jsapiTicket);
redisTemplate.opsForValue().set("zqs"+appid, jsapiTicket, expr,TimeUnit.MILLISECONDS);
}
}
}
//釋放鎖
redisLock.unlock(appid, value);
}