RedisTemplate執行lua腳本在Redis集群模式下報錯EvalSha is not supported in cluster environment.
異常信息:
org.springframework.dao.InvalidDataAccessApiUsageException: EvalSha is not supported in cluster environment.
at org.springframework.data.redis.connection.jedis.JedisClusterScriptingCommands.evalSha(JedisClusterScriptingCommands.java:83) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1318) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at org.springframework.data.redis.connection.DefaultStringRedisConnection.evalSha(DefaultStringRedisConnection.java:1729) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_181]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_181]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_181]
at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61) ~[spring-data-redis-2.1.8.RELEASE.jar:2.1.8.RELEASE]
at com.sun.proxy.$Proxy196.evalSha(Unknown Source) ~[?:?]
執行方法,集群下報錯:
public boolean unlock(String key, String value) {
Long result = template.execute(redisScript, Arrays.asList(key),Arrays.asList(value));
return RELEASE_SUCCESS.equals(result);
}
解決方案
原因:spring自帶的執行腳本方法中,集群模式直接拋出不支持執行腳本的異常,所以只能拿到原redis的connection來執行腳本。
public boolean unlock(String key, String value) {
//使用Lua腳本:先判斷是否是自己設置的鎖,再執行刪除
// 使用lua腳本刪除redis中匹配value的key,可以避免由於方法執行時間過長而redis鎖自動過期失效的時候誤刪其他線程的鎖
// spring自帶的執行腳本方法中,集群模式直接拋出不支持執行腳本的異常,所以只能拿到原redis的connection來執行腳本
List<String> keys = new ArrayList<>();
keys.add(key);
List<String> args = new ArrayList<>();
args.add(value);
Long result = template.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
Object nativeConnection = connection.getNativeConnection();
// 集群模式和單機模式雖然執行腳本的方法一樣,但是沒有共同的接口,所以只能分開執行
// 集群模式
if (nativeConnection instanceof JedisCluster) {
return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
}
// 單機模式
else if (nativeConnection instanceof Jedis) {
return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
}
return 0L;
}
});
//返回最終結果
return RELEASE_SUCCESS.equals(result);
}
參考文檔: