Рубрики
Без рубрики

Введение в Jedis – клиентская библиотека Java Redis

Введение в Jedis, клиентскую библиотеку на Java для Redis – популярного хранилища структур данных в памяти, которое также может сохраняться на диске.

Автор оригинала: baeldung.

1. Обзор

Эта статья представляет собой введение в Jedis , клиентскую библиотеку на Java для Redis – популярного хранилища структур данных в памяти, которое также может сохраняться на диске. Он управляется структурой данных на основе хранилища ключей для сохранения данных и может использоваться в качестве базы данных, кэша, брокера сообщений и т. Д.

Во-первых, мы собираемся объяснить, в каких ситуациях джедаи полезны и о чем идет речь.

В последующих разделах мы подробно рассмотрим различные структуры данных и объясним транзакции, конвейерную обработку и функцию публикации/подписки. Мы завершаем работу с пулом соединений и кластером Redis.

2. Почему джедаи?

Redis перечисляет наиболее известные клиентские библиотеки на своем официальном сайте . Существует множество альтернатив джедаям, но только еще две в настоящее время достойны их звезды рекомендации, салат и Редиссон .

У этих двух клиентов есть некоторые уникальные функции, такие как потокобезопасность, прозрачная обработка повторного подключения и асинхронный API, всех функций которого не хватает Jedis.

Однако он невелик и значительно быстрее, чем два других. Кроме того, это клиентская библиотека по выбору разработчиков Spring Framework, и у нее самое большое сообщество из всех трех.

3. Зависимости Maven

Давайте начнем с объявления единственной зависимости, которая нам понадобится в pom.xml :


    redis.clients
    jedis
    2.8.1

Если вы ищете последнюю версию библиотеки, проверьте эту страницу .

4. Установка Redis

Вам нужно будет установить и запустить одну из последних версий Redis. На данный момент мы запускаем последнюю стабильную версию (3.2.1), но любая версия post 3.x должна быть в порядке.

Найдите здесь дополнительную информацию о Redis для Linux и Macintosh, у них очень похожие основные шаги установки. Windows официально не поддерживается, но этот порт хорошо поддерживается.

После этого мы можем напрямую погрузиться в него и подключиться к нему из нашего Java-кода:

Jedis jedis = new Jedis();

Конструктор по умолчанию будет работать нормально, если вы не запустили службу на порту, отличном от порта по умолчанию, или на удаленной машине, и в этом случае вы можете правильно настроить ее, передав правильные значения в качестве параметров в конструктор.

5. Структуры Данных Redis

Большинство собственных команд операций поддерживаются, и, что достаточно удобно, они обычно имеют одно и то же имя метода.

5.1. Строки

Строки-это самый простой вид Redisvalue, полезный для тех случаев, когда вам нужно сохранить простые типы данных ключ-значение:

jedis.set("events/city/rome", "32,15,223,828");
String cachedResponse = jedis.get("events/city/rome");

Переменная кэшированный ответ будет содержать значение 32,15,223,828 . В сочетании с поддержкой истечения срока действия, обсуждаемой ниже, он может работать как молниеносный и простой в использовании уровень кэша для HTTP-запросов, полученных в вашем веб-приложении, и других требований к кэшированию.

5.2. Списки

Списки Redis-это просто списки строк, отсортированных по порядку вставки, что делает его идеальным инструментом для реализации, например, очередей сообщений:

jedis.lpush("queue#tasks", "firstTask");
jedis.lpush("queue#tasks", "secondTask");

String task = jedis.rpop("queue#tasks");

Переменная task будет содержать значение first Task . Помните, что вы можете сериализовать любой объект и сохранить его в виде строки, чтобы сообщения в очереди при необходимости могли содержать более сложные данные.

5.3. Наборы

Наборы Redis-это неупорядоченная коллекция строк, которая пригодится, если вы хотите исключить повторяющиеся элементы:

jedis.sadd("nicknames", "nickname#1");
jedis.sadd("nicknames", "nickname#2");
jedis.sadd("nicknames", "nickname#1");

Set nicknames = jedis.smembers("nicknames");
boolean exists = jedis.sismember("nicknames", "nickname#1");

Набор Java nicknames будет иметь размер 2, второе добавление nickname#1 было проигнорировано. Кроме того, переменная exists будет иметь значение true , метод dismember позволяет быстро проверить наличие определенного элемента.

5.4. Хэши

Хэши Redis сопоставляются между полями String и значениями String :

jedis.hset("user#1", "name", "Peter");
jedis.hset("user#1", "job", "politician");
		
String name = jedis.hget("user#1", "name");
		
Map fields = jedis.hgetAll("user#1");
String job = fields.get("job");

Как вы можете видеть, хэши-это очень удобный тип данных, когда вы хотите получить доступ к свойствам объекта по отдельности, поскольку вам не нужно извлекать весь объект целиком.

5.5. Сортированные наборы

Сортированные наборы подобны набору, в котором каждый член имеет связанный рейтинг, который используется для их сортировки:

Map scores = new HashMap<>();

scores.put("PlayerOne", 3000.0);
scores.put("PlayerTwo", 1500.0);
scores.put("PlayerThree", 8200.0);

scores.entrySet().forEach(playerScore -> {
    jedis.zadd(key, playerScore.getValue(), playerScore.getKey());
});
		
String player = jedis.zrevrange("ranking", 0, 1).iterator().next();
long rank = jedis.zrevrank("ranking", "PlayerOne");

Переменная player будет содержать значение PlayerThree потому что мы извлекаем первого игрока, и он тот, у кого самый высокий балл. Переменная rank будет иметь значение 1, потому что Игрок Один является вторым в рейтинге, а рейтинг основан на нуле.

6. Сделки

Транзакции гарантируют атомарность и потокобезопасность операций, что означает, что запросы от других клиентов никогда не будут обрабатываться одновременно во время транзакций Redis:

String friendsPrefix = "friends#";
String userOneId = "4352523";
String userTwoId = "5552321";

Transaction t = jedis.multi();
t.sadd(friendsPrefix + userOneId, userTwoId);
t.sadd(friendsPrefix + userTwoId, userOneId);
t.exec();

Вы даже можете сделать успех транзакции зависящим от определенного ключа, “наблюдая” за ним прямо перед созданием экземпляра вашей Транзакции :

jedis.watch("friends#deleted#" + userOneId);

Если значение этого ключа изменится до выполнения транзакции, транзакция не будет успешно завершена.

7. Конвейеризация

Когда нам нужно отправить несколько команд, мы можем упаковать их вместе в один запрос и сэкономить накладные расходы на соединение с помощью конвейеров, по сути, это оптимизация сети. До тех пор, пока операции взаимно независимы, мы можем воспользоваться этой техникой:

String userOneId = "4352523";
String userTwoId = "4849888";

Pipeline p = jedis.pipelined();
p.sadd("searched#" + userOneId, "paris");
p.zadd("ranking", 126, userOneId);
p.zadd("ranking", 325, userTwoId);
Response pipeExists = p.sismember("searched#" + userOneId, "paris");
Response> pipeRanking = p.zrange("ranking", 0, -1);
p.sync();

String exists = pipeExists.get();
Set ranking = pipeRanking.get();

Обратите внимание, что мы не получаем прямого доступа к ответам команд, вместо этого нам предоставляется экземпляр Response , из которого мы можем запросить базовый ответ после синхронизации конвейера.

8. Публикация/Подписка

Мы можем использовать функцию брокера обмена сообщениями Redis для отправки сообщений между различными компонентами нашей системы. Убедитесь, что потоки подписчика и издателя не используют одно и то же соединение Jedis.

8.1. Подписчик

Подписывайтесь и слушайте сообщения, отправленные на канал:

Jedis jSubscriber = new Jedis();
jSubscriber.subscribe(new JedisPubSub() {
    @Override
    public void onMessage(String channel, String message) {
        // handle message
    }
}, "channel");

Подписка-это метод блокировки, вам нужно будет явно отказаться от подписки на JedisPubSub . Мы переопределили метод onMessage , но есть еще много полезных методов , доступных для переопределения.

8.2. Издатель

Затем просто отправляйте сообщения на тот же канал из потока издателя:

Jedis jPublisher = new Jedis();
jPublisher.publish("channel", "test message");

9. Объединение Пулов Соединений

Важно знать, что то, как мы обращаемся с нашим примером джедаев, наивно. В реальном сценарии вы не хотите использовать один экземпляр в многопоточной среде, поскольку один экземпляр не является потокобезопасным.

К счастью, мы можем легко создать пул подключений к Redis для повторного использования по требованию, пул, который является потокобезопасным и надежным, если вы вернете ресурс в пул, когда закончите с ним.

Давайте создадим JedisPool :

final JedisPoolConfig poolConfig = buildPoolConfig();
JedisPool jedisPool = new JedisPool(poolConfig, "localhost");

private JedisPoolConfig buildPoolConfig() {
    final JedisPoolConfig poolConfig = new JedisPoolConfig();
    poolConfig.setMaxTotal(128);
    poolConfig.setMaxIdle(128);
    poolConfig.setMinIdle(16);
    poolConfig.setTestOnBorrow(true);
    poolConfig.setTestOnReturn(true);
    poolConfig.setTestWhileIdle(true);
    poolConfig.setMinEvictableIdleTimeMillis(Duration.ofSeconds(60).toMillis());
    poolConfig.setTimeBetweenEvictionRunsMillis(Duration.ofSeconds(30).toMillis());
    poolConfig.setNumTestsPerEvictionRun(3);
    poolConfig.setBlockWhenExhausted(true);
    return poolConfig;
}

Поскольку экземпляр пула является потокобезопасным, вы можете хранить его где-то статически, но вы должны позаботиться об уничтожении пула, чтобы избежать утечек при завершении работы приложения.

Теперь мы можем использовать наш пул из любой точки приложения, когда это необходимо:

try (Jedis jedis = jedisPool.getResource()) {
    // do operations with jedis resource
}

Мы использовали инструкцию Java try-with-resources, чтобы избежать необходимости вручную закрывать ресурс Jedis, но если вы не можете использовать эту инструкцию, вы также можете закрыть ресурс вручную в предложении finally .

Убедитесь, что вы используете пул, как мы описали в вашем приложении, если вы не хотите столкнуться с неприятными проблемами многопоточности. Очевидно, вы можете играть с параметрами конфигурации пула, чтобы адаптировать его к лучшим настройкам в вашей системе.

10. Кластер Redis

Эта реализация Redis обеспечивает легкую масштабируемость и высокую доступность, мы рекомендуем вам прочитать их официальную спецификацию , если вы с ней не знакомы. Мы не будем рассматривать настройку кластера Redis, поскольку это немного выходит за рамки этой статьи, но у вас не должно возникнуть проблем с этим, когда вы закончите с документацией.

Как только мы будем готовы, мы сможем начать использовать его из нашего приложения:

try (JedisCluster jedisCluster = new JedisCluster(new HostAndPort("localhost", 6379))) {
    // use the jedisCluster resource as if it was a normal Jedis resource
} catch (IOException e) {}

Нам нужно только предоставить сведения о хосте и порте из одного из наших главных экземпляров, он автоматически обнаружит остальные экземпляры в кластере.

Это, безусловно, очень мощная функция, но это не серебряная пуля. При использовании кластера Redis вы не можете выполнять транзакции или использовать конвейеры-две важные функции, на которые полагаются многие приложения для обеспечения целостности данных.

Транзакции отключены, поскольку в кластеризованной среде ключи будут сохраняться в нескольких экземплярах. Атомарность операций и безопасность потоков не могут быть гарантированы для операций, связанных с выполнением команд в разных экземплярах.

Некоторые продвинутые стратегии создания ключей гарантируют, что данные, которые вам интересны, будут сохранены в одном и том же экземпляре, будут сохранены таким образом. Теоретически это должно позволить вам успешно выполнять транзакции, используя один из базовых экземпляров Jedis кластера Redis.

К сожалению, в настоящее время вы не можете узнать, в каком экземпляре Redis конкретный ключ сохраняется с помощью Jedis (который фактически поддерживается изначально Redis), поэтому вы не знаете, в каком из экземпляров вы должны выполнить операцию транзакции. Если вы заинтересованы в этом, вы можете найти более подробную информацию здесь .

11. Заключение

Подавляющее большинство функций Redis уже доступны в Jedis, и его развитие продвигается хорошими темпами.

Это дает вам возможность интегрировать мощный механизм хранения данных в памяти в ваше приложение без особых проблем, просто не забудьте настроить пул соединений, чтобы избежать проблем с безопасностью потоков.

Примеры кода можно найти в проекте GitHub .