RedisTemplate访问单机和集群
1. 背景
项目开发中,需要执行Lua脚本。因为测试环境的Redis是单节点部署,所以在开发中使用如下代码是成功执行的:
@Resource
StringRedisTemplate stringRedisTemplate;
public Object exec(RedisScript script, List<String>keys,Object... args){
return stringRedisTemplate.execute(script,keys,args);
}
2. 异常信息
但是准生产及生产环境的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)
3. 原因分析
RedisTemplate默认情况下拿到的connection是单机的:
调用链:
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.script.RedisScript<T>, java.util.List<K>, java.lang.Object...)
org.springframework.data.redis.core.script.DefaultScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript<T>, java.util.List<K>, java.lang.Object...)
org.springframework.data.redis.core.script.DefaultScriptExecutor#execute(org.springframework.data.redis.core.script.RedisScript<T>, org.springframework.data.redis.serializer.RedisSerializer<?>, org.springframework.data.redis.serializer.RedisSerializer<T>, java.util.List<K>, java.lang.Object...)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>, boolean)
org.springframework.data.redis.core.RedisTemplate#execute(org.springframework.data.redis.core.RedisCallback<T>, 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,获取到的连接是单机的
4. 解决方案
@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;
});
}
5. JedisCluster 和 Jedis 两种连接类型的区别


文章讲述了在项目开发中,使用RedisTemplate执行Lua脚本在单机环境成功,但在集群环境中因`EvalSha`不支持而引发异常。问题的原因在于RedisTemplate默认获取的是单机连接。解决方案是检查连接类型,分别针对JedisCluster和Jedis实例执行不同的脚本执行方式。
2854

被折叠的 条评论
为什么被折叠?



