redis集羣+JedisCluster+lua腳本實現分佈式鎖(轉)

https://blog.csdn.net/qq_20597727/article/details/85235602node

 

在這片文章中,使用Jedis clien進行lua腳本的相關操做,同時也使用一部分jedis提供的具備原子性set操做來完成值和過時時間的同時設置。使用lua腳本根本緣由也是爲了保證咱們兩個redis操做之間的原子性,使分佈式鎖更加可靠。redis

JedisCluster相關代碼配置
在博主的實現例子中使用redis集羣實現分佈式鎖,因此在開始分佈式鎖實現以前須要進行JedisCluster的相關配置。博主是在spring boot的下進行開發,JedisCluster須要作的配置以下。spring

首先是依賴包引入,以下代碼所示。緩存

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>網絡

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
加入必要的配置信息分佈式

#redis集羣鏈接配置
spring.redis.cluster.nodes=192.168.0.15:6379,192.168.0.15:6380,192.168.0.15:6381,192.168.0.15:6382,192.168.0.15:6383,192.168.0.15:6384
#redis
spring.redis.cluster.max-redirects=6
spring.redis.jedis.pool.max-active=80
spring.redis.jedis.pool.max-idle=30
spring.redis.jedis.pool.max-wait=2000s
spring.redis.jedis.pool.min-idle=10
1
2
3
4
5
6
7
8
其次就是JedisCluster的配置方式,單機環境下Jedis也有相應的配置,在此很少說。JedisCluster配置以下。函數

/**
* @author zhoujy
* @date 2018年12月19日
**/
@Configuration
public class RedisDistributeLockConfig {spring-boot

@Value("${spring.redis.cluster.nodes}")
String redisNodes;oop

@Bean
//定義分佈式鎖對象,稍後講解實現
public RedisDistributeLock redisDistributeLock(JedisCluster jedisCluster){
return new RedisDistributeLock(jedisCluster);
}測試

@Bean
//定義JedisCluster操做bean
public JedisCluster jedisCluster(){
return new JedisCluster(pharseHostAnport());
}

private Set<HostAndPort> pharseHostAnport(){
if (StringUtils.isEmpty(redisNodes)){
throw new RuntimeException("redis nodes can't be null or empty");
}
String[] hps = redisNodes.split(",");
Set<HostAndPort> hostAndPorts = new HashSet<>();
for (String hp : hps) {
String[] hap = hp.split(":");
hostAndPorts.add(new HostAndPort(hap[0], Integer.parseInt(hap[1])));
}
return hostAndPorts;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
分佈式鎖實現
在完成JedisCluster的所需配置以後,能夠看看分佈式鎖的如何實現的

全部代碼以下所示。

/**
* JedisCluster + lua腳本實現分佈式鎖
* @author zhoujy
* @date 2018年12月19日
**/
public class RedisDistributeLock {

private Logger logger = LoggerFactory.getLogger(RedisDistributeLock.class);

private JedisCluster jedisCluster;

/**
* lua腳本:判斷鎖住值是否爲當前線程持有,是的話解鎖,不是的話解鎖失敗
*/
private static final String DISTRIBUTE_LOCK_SCRIPT_UNLOCK_VAL = "if" +
" redis.call('get', KEYS[1]) == ARGV[1]" +
" then" +
" return redis.call('del', KEYS[1])" +
" else" +
" return 0" +
" end";

private volatile String unlockSha1 = "";

private static final Long UNLOCK_SUCCESS_CODE = 1L;

private static final String LOCK_SUCCESS_CODE = "ok";

public RedisDistributeLock(JedisCluster jedisCluster) {
this.jedisCluster = jedisCluster;
}


/**
* 根據loopTryTime循環重試
* @param lockKey 鎖key
* @param lockVal 鎖值,用於解鎖校驗
* @param expiryTime 鎖過時時間
* @param loopTryTime 獲取失敗時,循環重試獲取鎖的時長
* @return 是否得到鎖
*/
public boolean tryLock(String lockKey, String lockVal, long expiryTime, long loopTryTime){
Long endTime = System.currentTimeMillis() + loopTryTime;
while (System.currentTimeMillis() < endTime){
if (tryLock(lockKey, lockVal, expiryTime)){
return true;
}
}
return false;
}

/**
* 根據loopTryTime循環重試
* @param lockKey 鎖key
* @param lockVal 鎖值,用於解鎖校驗
* @param expiryTime 鎖過時時間
* @param retryTimes 重試次數
* @param setpTime 每次重試間隔 mills
* @return 是否得到鎖
*/
public boolean tryLock(String lockKey, String lockVal, long expiryTime, int retryTimes, long setpTime){
while (retryTimes > 0){
if (tryLock(lockKey, lockVal, expiryTime)){
return true;
}
retryTimes--;
try {
Thread.sleep(setpTime);
} catch (InterruptedException e) {
logger.error("get distribute lock error" +e.getLocalizedMessage());
}
}
return false;
}

/**
* 一次嘗試,快速失敗。不支持重入
* @param lockKey 鎖key
* @param lockVal 鎖值,用於解鎖校驗
* @param expiryTime 鎖過時時間 MILLS
* @return 是否得到鎖
*/
public boolean tryLock(String lockKey, String lockVal, long expiryTime){
//相比通常的分佈式鎖,這裏把setNx和setExpiry操做合併到一塊兒,jedis保證原子性,避免連個命令之間出現宕機等問題
//這裏也能夠咱們使用lua腳本實現
String result = jedisCluster.set(lockKey, lockVal, "NX", "PX", expiryTime);
return LOCK_SUCCESS_CODE.equalsIgnoreCase(result);
}

/**
* 釋放分佈式鎖,釋放失敗最多是業務執行時間長於lockKey過時時間,應當結合業務場景調整過時時間
* @param lockKey 鎖key
* @param lockVal 鎖值
* @return 是否釋放成功
*/
public boolean tryUnLock(String lockKey, String lockVal){
List<String> keys = new ArrayList<>();
keys.add(lockKey);
List<String> argv = new ArrayList<>();
argv.add(lockVal);
try {
Object result = jedisCluster.evalsha(unlockSha1, keys, argv);
return UNLOCK_SUCCESS_CODE.equals(result);
}catch (JedisNoScriptException e){
//沒有腳本緩存時,從新發送緩存
logger.info("try to store script......");
storeScript(lockKey);
Object result = jedisCluster.evalsha(unlockSha1, keys, argv);
return UNLOCK_SUCCESS_CODE.equals(result);
}catch (Exception e){
e.printStackTrace();
return false;
}
}

/**
* 因爲使用redis集羣,所以每一個節點都須要各自緩存一份腳本數據
* @param slotKey 用來定位對應的slot的slotKey
*/
public void storeScript(String slotKey){
if (StringUtils.isEmpty(unlockSha1) || !jedisCluster.scriptExists(unlockSha1, slotKey)){
//redis支持腳本緩存,返回哈希碼,後續能夠繼續用來調用腳本
unlockSha1 = jedisCluster.scriptLoad(DISTRIBUTE_LOCK_SCRIPT_UNLOCK_VAL, slotKey);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
針對上面的代碼,逐步分析。

加鎖操做
相比通常的redis分佈式鎖,這裏操做jedis的操做方式進行加鎖,好處就是Jedis保證set與設置有效期兩個操做之間的原子性,避免在set值以後,程序宕機,致使沒有設置過時時間,鎖就一直被鎖住。

這一步操做咱們單獨使用lua腳本實現也能夠,可是幸虧jedis已經幫咱們進行實現。

/**
* 一次嘗試,快速失敗。不支持重入
* @param lockKey 鎖key
* @param lockVal 鎖值,用於解鎖校驗
* @param expiryTime 鎖過時時間 MILLS
* @return 是否得到鎖
*/
public boolean tryLock(String lockKey, String lockVal, long expiryTime){
//相比通常的分佈式鎖,這裏把setNx和setExpiry操做合併到一塊兒,jedis保證原子性,避免連個命令之間出現宕機等問題
//這裏也能夠咱們使用lua腳本實現
//NX表示setNX操做,PX表示過時時間是mills
String result = jedisCluster.set(lockKey, lockVal, "NX", "PX", expiryTime);
return LOCK_SUCCESS_CODE.equalsIgnoreCase(result);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
同時加鎖操做也有幾個簡單的重載實現,分別是重試獲取和循環獲取鎖的重載,根據業務場景適當調整使用。

解鎖操做
這裏的分佈式鎖的解鎖操做使用lua腳本幫助實現。

咱們都知道,分佈式鎖在解鎖時必定須要驗證是否是鎖的持有者,這種狀況下,咱們須要進行的操做就有獲取key的對應value,而後驗證value的值,這個過程,存在一種狀況,致使誤刪別的持有者的鎖。分析以下的操做順序圖

 

 

上面的操做順序可能出錯的狀況就是當lock1嘗試釋放時,先獲取值,判斷是不是鎖的持有者,若是是,就再發指令刪除鎖。這個過程可能存在問題就是,lock1在獲取值以後,恰好到了有效期了,那麼鎖可能會在此時被鎖競爭者2得到,而且設置鎖lock2,然而這時鎖競爭者1刪除鎖的指令恰好從新發送到redis-server,就會誤刪lock2,致使後續會被其餘鎖競爭者3獲取,發送不可知業務錯誤。

使用lua腳本的好處就是保證redis指令之間執行的原子性,把get和del執行放在腳本中,保證不會誤刪別的鎖競爭者的鎖,假如恰好出現get以後鎖值過時,最多就是del操做結果爲0,不會出現誤刪結果。

/**
* 釋放分佈式鎖,釋放失敗最多是業務執行時間長於lockKey過時時間,應當結合業務場景調整過時時間
* @param lockKey 鎖key
* @param lockVal 鎖值
* @return 是否釋放成功
*/
public boolean tryUnLock(String lockKey, String lockVal){
List<String> keys = new ArrayList<>();
keys.add(lockKey);
List<String> argv = new ArrayList<>();
argv.add(lockVal);
try {
Object result = jedisCluster.evalsha(unlockSha1, keys, argv);
return UNLOCK_SUCCESS_CODE.equals(result);
}catch (JedisNoScriptException e){
//沒有腳本緩存時,從新發送腳本並緩存
logger.info("try to store script......");
storeScript(lockKey);
//重試獲取
Object result = jedisCluster.evalsha(unlockSha1, keys, argv);
return UNLOCK_SUCCESS_CODE.equals(result);
}catch (Exception e){
e.printStackTrace();
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
解鎖腳本

/**
* lua腳本:判斷鎖住值是否爲當前線程持有,是的話解鎖,不是的話解鎖失敗
*/
private static final String DISTRIBUTE_LOCK_SCRIPT_UNLOCK_VAL = "if" +
" redis.call('get', KEYS[1]) == ARGV[1]" +
" then" +
" return redis.call('del', KEYS[1])" +
" else" +
" return 0" +
" end";
1
2
3
4
5
6
7
8
9
10
lua腳本緩存
在redis集羣中,爲了不重複發送腳本數據浪費網絡資源,可使用script load命令進行腳本數據緩存,而且返回一個哈希碼做爲腳本的調用句柄,每次調用腳本只須要發送哈希碼來調用便可。

127.0.0.1:6381> script load "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"
"e9f69f2beb755be68b5e456ee2ce9aadfbc4ebf4"
1
2
上面是在redis-cli中緩存腳本的方式,在程序中,存儲lua腳本的方式是以下所示。使用jedis能夠很方便就完成腳本緩存,先判斷腳本緩存是否存在,不存在就進行腳本數據緩存而且保存哈希碼,以備接下來調用腳本。

注意事項:須要注意的是,在redis集羣環境下,每一個節點都須要進行一份腳本緩存,不然就會出現

NOSCRIPT No matching script. Please use EVAL.
1
錯誤,所以我在程序中加了處理。

/**
* 因爲使用redis集羣,所以每一個節點都須要各自緩存一份腳本數據
* @param slotKey 用來定位對應的slot的slotKey
*/
public void storeScript(String slotKey){
if (StringUtils.isEmpty(unlockSha1) || !jedisCluster.scriptExists(unlockSha1, slotKey)){
//redis支持腳本緩存,返回哈希碼,後續能夠繼續用來調用腳本
unlockSha1 = jedisCluster.scriptLoad(DISTRIBUTE_LOCK_SCRIPT_UNLOCK_VAL, slotKey);
}
}
1
2
3
4
5
6
7
8
9
10
slotKey就是咱們set值時的key,redis根據crc16函數 計算key應該對應哪個slot,若是slot所在的redis節點沒有緩存腳本數據就會報處NOSCRIPT No matching script. Please use EVAL.異常,所以當捕捉到這個異常時,咱們在代碼中從新發送腳本數據進行緩存便可。

/**
* 釋放分佈式鎖,釋放失敗最多是業務執行時間長於lockKey過時時間,應當結合業務場景調整過時時間
* @param lockKey 鎖key
* @param lockVal 鎖值
* @return 是否釋放成功
*/
public boolean tryUnLock(String lockKey, String lockVal){
List<String> keys = new ArrayList<>();
keys.add(lockKey);
List<String> argv = new ArrayList<>();
argv.add(lockVal);
try {
Object result = jedisCluster.evalsha(unlockSha1, keys, argv);
return UNLOCK_SUCCESS_CODE.equals(result);
}catch (JedisNoScriptException e){
//沒有腳本緩存時,從新發送腳本並緩存
//根據lockkey計算slot,在對應redis節點從新緩存一份腳本數據
logger.info("try to store script......");
storeScript(lockKey);
//重試獲取
Object result = jedisCluster.evalsha(unlockSha1, keys, argv);
return UNLOCK_SUCCESS_CODE.equals(result);
}catch (Exception e){
e.printStackTrace();
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
以上就是redis集羣+lua腳本實現分佈式鎖的方式。

測試用例
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ActivityServiceApplication.class})
@Slf4j
public class ActivityServiceApplicationTests {

@Resource
private RedisDistributeLock redisDistributeLock;
@Test
public void testRedislock() throws InterruptedException {
for(int i=0;i < 50;i++){
int finalI = i;
new Thread(() ->{
if (redisDistributeLock.tryLock("TEST_LOCK_KEY", "TEST_LOCK_VAL_"+ finalI, 1000* 100, 1000*20)){
try {
log.warn("get lock successfully with lock value:-----" + "TEST_LOCK_VAL_"+ finalI);
Thread.sleep(2000);
if (!redisDistributeLock.tryUnLock("TEST_LOCK_KEY", "TEST_LOCK_VAL_"+ finalI)){
throw new RuntimeException("release lock fail");
}
log.warn("release lock successfully with lock value:-----" + "TEST_LOCK_VAL_"+ finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
log.warn("get lock fail with lock value:-----" + "TEST_LOCK_VAL_"+ finalI);
}
}).start();
}

Thread.sleep(1000*1000);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
設置50個線程嘗試獲取分佈式鎖,每一個線程嘗試時間爲20秒;獲取到鎖的線程,sleep2秒,而後釋放鎖。

最終會出現,10個線程可以依次得到鎖,40個線程獲取鎖超時失敗。

2018-12-24 15:35:16.462 WARN 42580 --- [ Thread-61] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_472018-12-24 15:35:18.710 WARN 42580 --- [ Thread-61] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_472018-12-24 15:35:18.711 WARN 42580 --- [ Thread-14] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_02018-12-24 15:35:20.788 WARN 42580 --- [ Thread-14] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_02018-12-24 15:35:20.789 WARN 42580 --- [ Thread-51] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_372018-12-24 15:35:22.831 WARN 42580 --- [ Thread-47] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_332018-12-24 15:35:22.831 WARN 42580 --- [ Thread-51] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_372018-12-24 15:35:25.177 WARN 42580 --- [ Thread-47] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_332018-12-24 15:35:25.177 WARN 42580 --- [ Thread-55] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_412018-12-24 15:35:27.227 WARN 42580 --- [ Thread-55] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_412018-12-24 15:35:27.228 WARN 42580 --- [ Thread-36] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_222018-12-24 15:35:29.586 WARN 42580 --- [ Thread-36] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_222018-12-24 15:35:29.587 WARN 42580 --- [ Thread-35] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_212018-12-24 15:35:31.609 WARN 42580 --- [ Thread-35] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_212018-12-24 15:35:31.610 WARN 42580 --- [ Thread-28] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_142018-12-24 15:35:34.071 WARN 42580 --- [ Thread-28] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_142018-12-24 15:35:34.071 WARN 42580 --- [ Thread-31] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_172018-12-24 15:35:36.089 WARN 42580 --- [ Thread-31] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_172018-12-24 15:35:36.089 WARN 42580 --- [ Thread-54] g.learn.ActivityServiceApplicationTests : get lock successfully with lock value:-----TEST_LOCK_VAL_402018-12-24 15:35:36.449 WARN 42580 --- [ Thread-60] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_462018-12-24 15:35:36.450 WARN 42580 --- [ Thread-52] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_382018-12-24 15:35:36.450 WARN 42580 --- [ Thread-56] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_422018-12-24 15:35:36.450 WARN 42580 --- [ Thread-59] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_452018-12-24 15:35:36.453 WARN 42580 --- [ Thread-38] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_242018-12-24 15:35:36.453 WARN 42580 --- [ Thread-37] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_232018-12-24 15:35:36.453 WARN 42580 --- [ Thread-45] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_312018-12-24 15:35:36.453 WARN 42580 --- [ Thread-43] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_292018-12-24 15:35:36.453 WARN 42580 --- [ Thread-49] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_352018-12-24 15:35:36.453 WARN 42580 --- [ Thread-39] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_252018-12-24 15:35:36.453 WARN 42580 --- [ Thread-34] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_202018-12-24 15:35:36.454 WARN 42580 --- [ Thread-16] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_22018-12-24 15:35:36.454 WARN 42580 --- [ Thread-26] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_122018-12-24 15:35:36.454 WARN 42580 --- [ Thread-21] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_72018-12-24 15:35:36.454 WARN 42580 --- [ Thread-22] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_82018-12-24 15:35:36.454 WARN 42580 --- [ Thread-15] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_12018-12-24 15:35:36.454 WARN 42580 --- [ Thread-27] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_132018-12-24 15:35:36.454 WARN 42580 --- [ Thread-17] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_32018-12-24 15:35:36.455 WARN 42580 --- [ Thread-25] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_112018-12-24 15:35:36.455 WARN 42580 --- [ Thread-62] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_482018-12-24 15:35:36.455 WARN 42580 --- [ Thread-57] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_432018-12-24 15:35:36.455 WARN 42580 --- [ Thread-40] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_262018-12-24 15:35:36.455 WARN 42580 --- [ Thread-33] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_192018-12-24 15:35:36.455 WARN 42580 --- [ Thread-30] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_162018-12-24 15:35:36.455 WARN 42580 --- [ Thread-41] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_272018-12-24 15:35:36.455 WARN 42580 --- [ Thread-42] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_282018-12-24 15:35:36.456 WARN 42580 --- [ Thread-48] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_342018-12-24 15:35:36.456 WARN 42580 --- [ Thread-63] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_492018-12-24 15:35:36.456 WARN 42580 --- [ Thread-29] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_152018-12-24 15:35:36.456 WARN 42580 --- [ Thread-32] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_182018-12-24 15:35:36.456 WARN 42580 --- [ Thread-50] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_362018-12-24 15:35:36.456 WARN 42580 --- [ Thread-46] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_322018-12-24 15:35:36.456 WARN 42580 --- [ Thread-19] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_52018-12-24 15:35:36.456 WARN 42580 --- [ Thread-20] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_62018-12-24 15:35:36.456 WARN 42580 --- [ Thread-53] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_392018-12-24 15:35:36.457 WARN 42580 --- [ Thread-23] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_92018-12-24 15:35:36.457 WARN 42580 --- [ Thread-58] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_442018-12-24 15:35:36.457 WARN 42580 --- [ Thread-18] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_42018-12-24 15:35:36.457 WARN 42580 --- [ Thread-24] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_102018-12-24 15:35:36.457 WARN 42580 --- [ Thread-44] g.learn.ActivityServiceApplicationTests : get lock fail with lock value:-----TEST_LOCK_VAL_302018-12-24 15:35:38.091 WARN 42580 --- [ Thread-54] g.learn.ActivityServiceApplicationTests : release lock successfully with lock value:-----TEST_LOCK_VAL_40

相關文章
相關標籤/搜索