乐趣区

关于redis:聊聊RespCommand

本文次要钻研一下 RespCommand

RespCommand

resp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/RespCommand.java

@FunctionalInterface
public interface RespCommand {RedisToken execute(Request request);
}
  • RespCommand 定义了 execute 办法,接管 Request 参数,返回 RedisToken

Request

resp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/Request.java

public interface Request {String getCommand();
  ImmutableArray<SafeString> getParams();
  SafeString getParam(int i);
  Option<SafeString> getOptionalParam(int i);
  int getLength();
  boolean isEmpty();
  Session getSession();
  ServerContext getServerContext();
  boolean isExit();}
  • Request 接口定义了 getCommand、getParams、getParam、getOptionalParam、getLength、isEmpty、getSession、getServerContext、isExit 办法

DefaultRequest

resp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/DefaultRequest.java

public class DefaultRequest implements Request {

  private final SafeString command;

  private final ImmutableArray<SafeString> params;

  private final Session session;

  private final ServerContext server;

  public DefaultRequest(ServerContext server, Session session, SafeString command, ImmutableArray<SafeString> params) {
    this.server = server;
    this.session = session;
    this.command = requireNonNull(command);
    this.params = requireNonNull(params);
  }

  @Override
  public String getCommand() {return command.toString();
  }

  @Override
  public ImmutableArray<SafeString> getParams() {return params;}

  @Override
  public SafeString getParam(int i) {if (i < params.size()) {return params.get(i);
    }
    return null;
  }

  @Override
  public Option<SafeString> getOptionalParam(int i) {return Option.of(() -> getParam(i));
  }

  @Override
  public int getLength() {return params.size();
  }

  @Override
  public boolean isEmpty() {return params.isEmpty();
  }

  @Override
  public boolean isExit() {return command.toString().equalsIgnoreCase("quit");
  }

  @Override
  public Session getSession() {return session;}

  @Override
  public ServerContext getServerContext() {return server;}

  @Override
  public String toString() {return command + "[" + params.size() + "]:" + params;
  }
}
  • DefaultRequest 实现了 Request 接口,它定义了 command、params、session、server 属性,均在结构器中传入

CommandSuite

resp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/CommandSuite.java

public class CommandSuite {private static final Logger LOGGER = LoggerFactory.getLogger(CommandSuite.class);

  private final Map<String, Class<?>> metadata = new HashMap<>();
  private final Map<String, RespCommand> commands = new HashMap<>();

  private final NullCommand nullCommand = new NullCommand();

  private final CommandWrapperFactory factory;

  public CommandSuite() {this(new DefaultCommandWrapperFactory());
  }

  public CommandSuite(CommandWrapperFactory factory) {
    this.factory = factory;
    addCommand(PingCommand.class);
    addCommand(EchoCommand.class);
    addCommand(QuitCommand.class);
    addCommand(TimeCommand.class);
  }

  public RespCommand getCommand(String name) {return commands.getOrDefault(name.toLowerCase(), nullCommand);
  }

  public boolean isPresent(String name, Class<? extends Annotation> annotationClass) {return getMetadata(name).isAnnotationPresent(annotationClass);
  }

  public boolean contains(String name) {return commands.get(name) != null;
  }

  protected void addCommand(Class<?> clazz) {Try.of(clazz::newInstance)
       .onSuccess(this::processCommand)
       .onFailure(e -> LOGGER.error("error loading command:" + clazz.getName(), e));
  }

  protected void addCommand(String name, RespCommand command) {commands.put(name.toLowerCase(), factory.wrap(command));
  }

  private void processCommand(Object command) {Class<?> clazz = command.getClass();
    Command annotation = clazz.getAnnotation(Command.class);
    if (annotation != null) {commands.put(annotation.value(), factory.wrap(command));
      metadata.put(annotation.value(), clazz);
    } else {LOGGER.warn("annotation not present at {}", clazz.getName());
    }
  }

  private Class<?> getMetadata(String name) {return metadata.getOrDefault(name.toLowerCase(), Void.class);
  }
}
  • CommandSuite 的结构器增加了 PingCommand、EchoCommand、QuitCommand、TimeCommand 四个 command;addCommand 办法将 factory.wrap(command) 的命令放入 commands 中

CommandWrapperFactory

resp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/CommandWrapperFactory.java

public interface CommandWrapperFactory {RespCommand wrap(Object command);
}
  • CommandWrapperFactory 定义了 wrap 办法,将 command 包装为 RespCommand

DefaultCommandWrapperFactory

resp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/DefaultCommandWrapperFactory.java

public class DefaultCommandWrapperFactory implements CommandWrapperFactory {
  @Override
  public RespCommand wrap(Object command) {if (command instanceof RespCommand) {return new CommandWrapper((RespCommand) command);
    }
    throw new IllegalArgumentException("must implements command interface");
  }
}
  • DefaultCommandWrapperFactory 实现了 CommandWrapperFactory 接口,其 wrap 办法应用 CommandWrapper 包装 RespCommand

CommandWrapper

resp-server-0.16.0/src/main/java/com/github/tonivade/resp/command/CommandWrapper.java

public class CommandWrapper implements RespCommand {

  private int params;

  private final RespCommand command;

  public CommandWrapper(RespCommand command) {
    this.command = command;
    ParamLength length = command.getClass().getAnnotation(ParamLength.class);
    if (length != null) {this.params = length.value();
    }
  }

  @Override
  public RedisToken execute(Request request) {if (request.getLength() < params) {return error("ERR wrong number of arguments for'" + request.getCommand() + "'command");
    }
    return command.execute(request);
  }
}
  • CommandWrapper 会读取 ParamLength 注解,若读的到则将注解的 value 值赋值给 params,用于 execute 办法对 request 的参数长度进行校验

小结

RespCommand 定义了 execute 办法,接管 Request 参数,返回 RedisToken;CommandSuite 的结构器增加了 PingCommand、EchoCommand、QuitCommand、TimeCommand 四个 command;addCommand 办法将 factory.wrap(command) 的命令放入 commands 中;DefaultCommandWrapperFactory 实现了 CommandWrapperFactory 接口,其 wrap 办法应用 CommandWrapper 包装 RespCommand;CommandWrapper 会读取 ParamLength 注解,若读的到则将注解的 value 值赋值给 params,用于 execute 办法对 request 的参数长度进行校验

doc

  • RespCommand
退出移动版