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);
    }

参考文档:

https://my.oschina.net/dengfuwei/blog/1600681

在使用Spring Boot的`RedisTemplate`执行Redis脚本时,如果遇到错误提示“EvalSha is not support in cluster mode”,这是因为在集群模式下,Redis的`EVALSHA`命令是不被支持的。`EVALSHA`命令用于执行预加载的Lua脚本,但在集群模式下,Redis集群会将键分布到不同的节点上,`EVALSHA`命令无法在这种情况下正常工作。 要解决这个问题,可以采取以下几种方法: 1. **使用`EVAL`命令**:直接使用`EVAL`命令而不是`EVALSHA`来执行Lua脚本。`EVAL`命令会每次都发送脚本内容,虽然会增加网络传输的开销,但可以在集群模式下正常工作。 2. **在每个节点上预加载脚本**:确保每个节点上都预加载了相同的Lua脚本,这样可以使用`EVALSHA`命令。可以使用`SCRIPT LOAD`命令在每个节点上预加载脚本。 3. **使用事务或流水线**:将多个命令打包成一个事务或流水线,减少网络往返次数。 以下是一个示例,展示了如何在Spring Boot中使用`RedisTemplate`执行Lua脚本: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service public class RedisService { @Autowired private RedisTemplate<String, Object> redisTemplate; public Object executeLuaScript(String script, List<String> keys, List<Object> args) { return redisTemplate.execute((RedisConnection connection) -> { return connection.eval(script.getBytes(), ScriptOutputType.VALUE, keys.toArray(new String[0]), args.toArray()); }); } } ``` 在这个示例中,`executeLuaScript`方法接收Lua脚本、键列表和参数列表,并使用`RedisTemplate`的`execute`方法执行脚本
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值