redis lua脚本

 

作用:使用lua脚本可将多条命令原子执行,可实现比redis事务更为强大的功能

注意:lua执行过程中,如果出错,已经执行的命令不会回滚

 

 

*********************

相关类与接口

 

RedisTemplate

public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {

    @Nullable
    private ScriptExecutor<K> scriptExecutor;

    ...


************
构造方法

    public RedisTemplate() {
    }

    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        boolean defaultUsed = false;
        if (this.defaultSerializer == null) {
            this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
        }

        if (this.enableDefaultSerializer) {
            if (this.keySerializer == null) {
                this.keySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.valueSerializer == null) {
                this.valueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashKeySerializer == null) {
                this.hashKeySerializer = this.defaultSerializer;
                defaultUsed = true;
            }

            if (this.hashValueSerializer == null) {
                this.hashValueSerializer = this.defaultSerializer;
                defaultUsed = true;
            }
        }

        if (this.enableDefaultSerializer && defaultUsed) {
            Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
        }

        if (this.scriptExecutor == null) {
            this.scriptExecutor = new DefaultScriptExecutor(this);
        }                        //默认使用DefaultScriptorExecutor执行脚本

        this.initialized = true;
    }


************
执行脚本相关方法

    public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
        return this.scriptExecutor.execute(script, keys, args);
    }

    public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args) {
        return this.scriptExecutor.execute(script, argsSerializer, resultSerializer, keys, args);
    }                        

    。。。。

}

 

 

RedisScript:redis脚本

public interface RedisScript<T> {
    String getSha1();

    @Nullable
    Class<T> getResultType();

    String getScriptAsString();

    default boolean returnsRawValue() {
        return this.getResultType() == null;
    }

    static <T> RedisScript<T> of(String script) {
        return new DefaultRedisScript(script);
    }//构造RedisScript,无返回值

    static <T> RedisScript of(String script, Class<T> resultType) {
        Assert.notNull(script, "Script must not be null!");
        Assert.notNull(resultType, "ResultType must not be null!");
        return new DefaultRedisScript(script, resultType);
    }//构造RedisScript,返回值得类型为resultType

    static <T> RedisScript<T> of(Resource resource) {
        Assert.notNull(resource, "Resource must not be null!");
        DefaultRedisScript<T> script = new DefaultRedisScript();
        script.setLocation(resource);
        return script;
    }

    static <T> RedisScript<T> of(Resource resource, Class<T> resultType) {
        Assert.notNull(resource, "Resource must not be null!");
        Assert.notNull(resultType, "ResultType must not be null!");
        DefaultRedisScript<T> script = new DefaultRedisScript();
        script.setResultType(resultType);
        script.setLocation(resource);
        return script;
    }
}

 

DefaultRedisScript

public class DefaultRedisScript<T> implements RedisScript<T>, InitializingBean {
    private final Object shaModifiedMonitor;
    @Nullable
    private ScriptSource scriptSource;
    @Nullable
    private String sha1;
    @Nullable
    private Class<T> resultType;


***********
构造方法

    public DefaultRedisScript() {
        this.shaModifiedMonitor = new Object();
    }

    public DefaultRedisScript(String script) {
        this(script, (Class)null);
    }

    public DefaultRedisScript(String script, @Nullable Class<T> resultType) {
        this.shaModifiedMonitor = new Object();
        this.setScriptText(script);
        this.resultType = resultType;
    }


***********
普通方法

    public void afterPropertiesSet() {
        Assert.state(this.scriptSource != null, "Either script, script location, or script source is required");
    }

    public String getSha1() {
        synchronized(this.shaModifiedMonitor) {
            if (this.sha1 == null || this.scriptSource.isModified()) {
                this.sha1 = DigestUtils.sha1DigestAsHex(this.getScriptAsString());
            }

            return this.sha1;
        }
    }//获取脚本的sha1值

    @Nullable
    public Class<T> getResultType() {
        return this.resultType;
    }//获得脚本的返回结果类型

    public String getScriptAsString() {
        try {
            return this.scriptSource.getScriptAsString();
        } catch (IOException var2) {
            throw new ScriptingException("Error reading script text", var2);
        }
    }//获得脚本内容,以string形式返回

    public void setResultType(@Nullable Class<T> resultType) {
        this.resultType = resultType;
    }//设置脚本的返回类型

    public void setScriptText(String scriptText) {
        this.scriptSource = new StaticScriptSource(scriptText);
    }//设置脚本内容

    public void setLocation(Resource scriptLocation) {
        this.scriptSource = new ResourceScriptSource(scriptLocation);
    }

    public void setScriptSource(ScriptSource scriptSource) {
        this.scriptSource = scriptSource;
    }
}

 

 

ScriptExecutor:脚本执行接口

public interface ScriptExecutor<K> {
    <T> T execute(RedisScript<T> var1, List<K> var2, Object... var3);

    <T> T execute(RedisScript<T> var1, RedisSerializer<?> var2, RedisSerializer<T> var3, List<K> var4, Object... var5);
}

 

DefaultScriptExecutor

public class DefaultScriptExecutor<K> implements ScriptExecutor<K> {
    private final RedisTemplate<K, ?> template;


********
构造方法

    public DefaultScriptExecutor(RedisTemplate<K, ?> template) {
        this.template = template;
    }


********
普通方法

    public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
        return this.execute(script, this.template.getValueSerializer(), this.template.getValueSerializer(), keys, args);
    }//默认使用redisTemplate的value序列化器序列化传递给脚本的args、反序列化返回结果

    public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args) {
        return this.template.execute((connection) -> {
            ReturnType returnType = ReturnType.fromJavaType(script.getResultType());
            byte[][] keysAndArgs = this.keysAndArgs(argsSerializer, keys, args);
            int keySize = keys != null ? keys.size() : 0;
            if (!connection.isPipelined() && !connection.isQueueing()) {
                return this.eval(connection, script, returnType, keySize, keysAndArgs, resultSerializer);
            } else {
                connection.eval(this.scriptBytes(script), returnType, keySize, keysAndArgs);
                return null;
            }
        });
    }//统一调用脚本的接口,使用argsSerializer序列化args、resultSerializer序列化返回结果

    protected <T> T eval(RedisConnection connection, RedisScript<T> script, ReturnType returnType, int numKeys, byte[][] keysAndArgs, RedisSerializer<T> resultSerializer) {
        Object result;
        try {
            result = connection.evalSha(script.getSha1(), returnType, numKeys, keysAndArgs);
        } catch (Exception var9) {
            if (!ScriptUtils.exceptionContainsNoScriptError(var9)) {
                throw var9 instanceof RuntimeException ? (RuntimeException)var9 : new RedisSystemException(var9.getMessage(), var9);
            }

            result = connection.eval(this.scriptBytes(script), returnType, numKeys, keysAndArgs);
        }

        return script.getResultType() == null ? null : this.deserializeResult(resultSerializer, result);
    }//底层具体执行脚本的方法

   protected byte[][] keysAndArgs(RedisSerializer argsSerializer, List<K> keys, Object[] args) {
        int keySize = keys != null ? keys.size() : 0;
        byte[][] keysAndArgs = new byte[args.length + keySize][];
        int i = 0;
        if (keys != null) {
            Iterator var7 = keys.iterator();

            label42:
            while(true) {
                while(true) {
                    if (!var7.hasNext()) {
                        break label42;
                    }

                    K key = var7.next();
                    if (this.keySerializer() == null && key instanceof byte[]) {
                        keysAndArgs[i++] = (byte[])((byte[])key);
                    } else {
                        keysAndArgs[i++] = this.keySerializer().serialize(key);
                    }
                }
            }
        }

        Object[] var11 = args;
        int var12 = args.length;

        for(int var9 = 0; var9 < var12; ++var9) {
            Object arg = var11[var9];
            if (argsSerializer == null && arg instanceof byte[]) {
                keysAndArgs[i++] = (byte[])((byte[])arg);
            } else {
                keysAndArgs[i++] = argsSerializer.serialize(arg);
            }
        }

        return keysAndArgs;
    }//将keys、args转换为字节数组,keys用redisTemplate的键序列化器序列化、args用argsSerializer序列化器序列化

    protected byte[] scriptBytes(RedisScript<?> script) {
        return this.template.getStringSerializer().serialize(script.getScriptAsString());
    }//将脚本转换为字节数组

    protected <T> T deserializeResult(RedisSerializer<T> resultSerializer, Object result) {
        return ScriptUtils.deserializeResult(resultSerializer, result);
    }//将脚本的返回结果反序列化

    protected RedisSerializer keySerializer() {
        return this.template.getKeySerializer();
    }//获得redisTemplate键的序列化器

}

 

 

更多推荐