乐趣区

关于redis:聊聊claudb的scripting-command

本文次要钻研一下 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
退出移动版