RedisTemplate执行lua脚本在Redis集群模式下报错:EvalSha is not supported in cluster environment.
执行方法,Redis是单节点执行成功但是集群下报错
public boolean unlock(String key, String value) { Long result = template.execute(redisScript, Arrays.asList(key),Arrays.asList(value)); return RELEASE_SUCCESS.equals(result); }
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) ~[?:?]
原因
RedisTemplate默认情况下拿到的connection是单机的
解决
集群模式直接抛出不支持执行脚本的异常,因为只能拿到原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); }
@Resource StringRedisTemplate stringRedisTemplate; public Object exec(RedisScript script, List<String>keys,Object... args){ // 将可变参转为 List List<String> list = new ArrayList<>(); for (Object arg : args) { list.add(arg.toString()); } // 将 RedisScript 转换为 string 类型的脚本 String scriptScriptAsString = script.getScriptAsString(); return stringRedisTemplate.execute((RedisCallback<Object>) connection -> { // 获取连接 Object nativeConnection = connection.getNativeConnection(); // 集群模式的实例 if (nativeConnection instanceof JedisCluster) { return (Long)((JedisCluster) nativeConnection).eval(scriptScriptAsString, keys, list); } // 单机模式的实例 if (nativeConnection instanceof Jedis) { return (Long)((Jedis) nativeConnection).eval(scriptScriptAsString, keys, list); } return null; }); }