序
本文次要钻研一下 claudb 的 scripting command
AbstractEvalCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/AbstractEvalCommand.java
abstract class AbstractEvalCommand implements DBCommand {
@Override
public RedisToken execute(Database db, Request request) {return script(request).map(script -> execute(request, script))
.getOrElse(error("NOSCRIPT No matching script. Please use EVAL"));
}
private RedisToken execute(Request request, SafeString script) {int numParams = parseInt(request.getParam(1).toString());
if (numParams + 2 > request.getLength()) {return error("invalid number of arguments");
}
List<SafeString> params = request.getParams().stream().skip(2).collect(toList());
List<SafeString> keys = readParams(numParams, params);
List<SafeString> argv = readArguments(numParams, params);
return LuaInterpreter.buildFor(request).execute(script, keys, argv);
}
protected abstract Option<SafeString> script(Request request);
private List<SafeString> readParams(int numParams, List<SafeString> params) {List<SafeString> keys = new LinkedList<>();
for (int i = 0; i < numParams; i++) {keys.add(params.get(i));
}
return keys;
}
private List<SafeString> readArguments(int numParams, List<SafeString> params) {List<SafeString> argv = new LinkedList<>();
for (int i = numParams; i < params.size(); i++) {argv.add(params.get(i));
}
return argv;
}
}
- AbstractEvalCommand 实现了 DBCommand 接口,其 execute 办法先通过子类实现的 script 办法获取 SafeString,而后再外部的 execute 办法执行脚本;execute 办法先解析 keys、argv,而后通过 LuaInterpreter.buildFor(request).execute(script, keys, argv) 执行 lua 脚本
EvalCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/EvalCommand.java
@Command("eval")
@ParamLength(2)
public class EvalCommand extends AbstractEvalCommand {
@Override
protected Option<SafeString> script(Request request) {return Option.some(request.getParam(0));
}
}
- EvalCommand 继承了 AbstractEvalCommand,其 script 办法取 request.getParam(0)
EvalShaCommand
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/EvalShaCommand.java
@Command("evalsha")
@ParamLength(2)
public class EvalShaCommand extends AbstractEvalCommand {
@Override
protected Option<SafeString> script(Request request) {DBServerState server = getServerState(request.getServerContext());
return server.getScript(request.getParam(0));
}
}
- EvalShaCommand 继承了 AbstractEvalCommand,其 script 办法先通过 getServerState(request.getServerContext()) 获取 server,再执行 server.getScript(request.getParam(0))
ScriptCommands
claudb-1.7.1/src/main/java/com/github/tonivade/claudb/command/scripting/ScriptCommands.java
@ParamLength(1)
@Command("script")
public class ScriptCommands implements DBCommand {
@Override
public RedisToken execute(Database db, Request request) {return Pattern1.<Request, RedisToken>build()
.when(isCommand("load"))
.then(this::load)
.when(isCommand("exists"))
.then(this::exists)
.when(isCommand("flush"))
.then(this::flush)
.otherwise()
.then(this::unknownCommand)
.apply(request);
}
private RedisToken unknownCommand(Request request) {return RedisToken.error("Unknown SCRIPT subcommand:" + request.getParam(0));
}
private RedisToken load(Request request) {SafeString script = request.getParam(1);
return Try.of(() -> digest(script)).map(sha1 -> {DBServerState server = getServerState(request.getServerContext());
server.saveScript(safeString(sha1), script);
return RedisToken.string(sha1);
}).getOrElse(RedisToken.error("ERR cannot generate sha1 sum for script:" + script));
}
private RedisToken exists(Request request) {DBServerState server = getServerState(request.getServerContext());
return integer(server.getScript(request.getParam(1)).isPresent());
}
private RedisToken flush(Request request) {getServerState(request.getServerContext()).cleanScripts();
return RedisToken.responseOk();}
private String digest(SafeString script) throws NoSuchAlgorithmException {MessageDigest digest = MessageDigest.getInstance("SHA-1");
return new SafeString(digest.digest(script.getBytes())).toHexString();}
private Matcher1<Request> isCommand(String command) {return request -> request.getParam(0).toString().toLowerCase().equals(command);
}
}
- ScriptCommands 实现了 DBCommand 接口,其 execute 办法反对 load、exists、flush 命令;其中 load 办法通过 server.saveScript(safeString(sha1), script) 来保留 script;其 exists 办法通过 server.getScript(request.getParam(1)).isPresent() 来判断 script 是否存在;其 flush 办法执行 getServerState(request.getServerContext()).cleanScripts()
小结
claudb scripting 相干的 command 有 EvalCommand、EvalShaCommand、ScriptCommands
doc
- command/scripting