项目开发中,需要执行Lua脚本。因为测试环境的Redis是单节点部署,所以在开发中使用如下代码是成功执行的:
@Resource
StringRedisTemplate stringRedisTemplate;
public Object exec(RedisScript script, List<String>keys,Object... args){
return stringRedisTemplate.execute(script,keys,args);
}
但是准生产及生产环境的Redis是集群部署,所以在使用上述代码时会报错,EvalSha不支持集群环境:
org.springframework.dao.InvalidDataAccessApiUsageException: EvalSha is not supported in cluster environment.
at org.springframework.data.redis.connection.jedis.JedisClusterScriptingCommands.evalSha(JedisClusterScriptingCommands.java:83)
at org.springframework.data.redis.connection.DefaultedRedisConnection.evalSha(DefaultedRedisConnection.java:1240)
at org.springframework.data.redis.connection.DefaultStringRedisConnection.evalSha(DefaultStringRedisConnection.java:1653)
at sun.reflect.GeneratedMethodAccessor173.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.data.redis.core.CloseSuppressingInvocationHandler.invoke(CloseSuppressingInvocationHandler.java:61)
at com.sun.proxy.$Proxy175.evalSha(Unknown Source)
at org.springframework.data.redis.core.script.DefaultScriptExecutor.eval(DefaultScriptExecutor.java:77)
at org.springframework.data.redis.core.script.DefaultScriptExecutor.lambda$execute$0(DefaultScriptExecutor.java:68)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:171)
at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:58)
at org.springframework.data.redis.core.script.DefaultScriptExecutor.execute(DefaultScriptExecutor.java:52)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:346)
RedisTemplate默认情况下拿到的connection是单机的:
调用链:
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.lang.Object...)
org.springframework.data.redis.core.script.DefaultScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript, java.util.List, java.lang.Object...)
org.springframework.data.redis.core.script.DefaultScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript, org.springframework.data.redis.serializer.RedisSerializer>, org.springframework.data.redis.serializer.RedisSerializer, java.util.List, java.lang.Object...)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback, boolean)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback, boolean, boolean)
org.springframework.data.redis.core.RedisConnectionUtils#getConnection(org.springframework.data.redis.connection.RedisConnectionFactory, boolean)
org.springframework.data.redis.core.RedisConnectionUtils#doGetConnection
org.springframework.data.redis.core.RedisConnectionUtils#fetchConnection
可以看到在 org.springframework.data.redis.core.RedisConnectionUtils#fetchConnection 方法中,如果配置的是单节点的redis,获取到的连接是单机的
@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;
});
}