Коротко о протоколе
Для соединения с сервером используем tcp-протокол, порт по умолчанию - 6379. Команды и наборы данных в ответах завершаются последовательностью "\r\n" (CRLF). Первый символ ответа указывает на тип возвращаемого значения. Тут ":" - число, "-" - сообщение об ошибке, "+" - однострочный ответ. Интереснее ответы, начинающиеся с "$". Это, так называемые "составные" ответы. Они содержат две строки, первая из которых - число байт, которые нужно прочитать во второй. Такие ответы будем получать наиболее часто, их возвращает команда GET для обычных ключей. Если же ключ необычный, например хэш, то ответ будет "многосоставной". В нём первый символ - "*", затем число результатов в ответе, затем результаты, каждый по две строке в виде составных ответов, начинающихся с "$".
Архитектура
В последней (на сегодня) версии Redis я насчитал более 120 команд. Из них в обычной практике понадобится не более десятка, но хорошая библиотека должна легко расширяться.
Схема работы будет такой: каждая команда будет представлена одним классом, в нём - специфическая логика по формированию запроса и интерпретации ответа. Сам метод, который пишет/читает данные будет реализован в их общем предке. Все классы будут реализовывать один и тот же абстрактный метод. Приложение о классах команд ничего знать не должно, для него у нас будет Factory, которое будет принимать запрос, подгружать в рантайме нужный класс, создавать его инстанс, инициализировать его (например отдавать ему уже открытый сокет) и дёргать его стандартный метод. В коде это выглядит так:
Так, во имя стандартизации мы параметры в запрос передаём как массив строк, а ответ получаем в виде такого себе объекта-оборотня Response:
- public class RedisFactory {
- private Socket s;
- public static final String COMMAND_AUTH = "redisui.connector.command.Auth";
- public static final String COMMAND_KEYS = "redisui.connector.command.Keys";
- public static final String COMMAND_GET = "redisui.connector.command.Get";
- public static final String COMMAND_TYPE = "redisui.connector.command.Type";
- public static final String COMMAND_HGETALL = "redisui.connector.command.Hgetall";
- public static final String COMMAND_DEL = "redisui.connector.command.Del";
- public void connect(String host, String port, String pass) throws Exception {
- String[] params = {host, port, pass};
- exec(COMMAND_AUTH, params);
- }
- public Response exec(String cmd, String[] params) throws Exception {
- Class<? extends CommandBased> command = Class.forName(cmd).asSubclass(CommandBased.class);
- CommandBased ci = command.newInstance();
- ci.setS(this.s);
- Response r = ci.exec(params);
- this.s = ci.getS();
- return r;
- }
- }
На стороне приложения мы либо знаем, какого типа будет ответ и в таком случае вызываем нужный getter, либо обрабатываем все варианты, получая тип конкретного ответа из getType().
- public class Response {
- private String type;
- private byte[] out;
- public static final String TYPE_LINE = "+";
- public static final String TYPE_ERROR = "-";
- public static final String TYPE_INT = ":";
- public static final String TYPE_STRING = "$";
- public static final String TYPE_ARRAY = "*";
- public Response(String type, byte[] out) {
- this.type = type;
- this.out = out;
- }
- public String getLineOut() {
- ...
- }
- public int getIntOut() {
- ...
- }
- public String getStringOut() {
- ...
- }
- public String[] getArrayOut() {
- ...
- }
- public String getType() {
- return type;
- }
- }
И, наконец, пример класса команды:
Как видим, добавлять новые команды в такую библиотеку - одно удовольствие.
- public class Get extends CommandBased {
- @Override
- public Response exec(String[] params) throws Exception {
- String command = "get "+params[0]+"\r\n";
- Response res = interrupt(command);
- if (res.getType().equals(Response.TYPE_ERROR)) {
- throw new Exception(res.getLineOut());
- }
- return res;
- }
- }
Результат:
За пару часов работы получился прототип, который ищет ключи по маске, позволяет просматривать их содержимое и удалять их.
По мере добавления функционала буду выкладывать новые версии у себя на сайте. Если кому пригодится - всегда на здоровье :).
Ваш сайт http://multipi.net/ не работает(
ОтветитьУдалитьБольше двух лет прошло. Тот проект потерял актуальность. Но специально для желающих разобраться в вопросе подробнее, я выгрузил RedisUI на GitHub. Клонировать и доработать при желании проект вы сможете отсюда: https://github.com/smmarat/RedisUI
ОтветитьУдалить