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键的序列化器
}