Перейти к основному содержимому

Руководство по работе с Redis через Redisson

· 12 мин. чтения

Задача: Наибольшая подстрока палиндром

Для заданной строки s, верните наибольшую подстроку палиндром входящую в s. Подстрока — это непрерывная непустая последовательность символов внутри строки. Стока является палиндромом, если она читается одинаково в обоих направлениях...

ANDROMEDA 42

1. Обзор

Redisson — это клиент Redis для Java . В этой статье мы рассмотрим некоторые его функции и продемонстрируем, как он может облегчить создание распределенных бизнес-приложений.

Redisson представляет собой сетку данных в памяти , которая предлагает распределенные объекты и службы Java, поддерживаемые Redis . Его распределенная модель данных в памяти позволяет совместно использовать объекты и службы предметной области между приложениями и серверами.

В этой статье мы увидим, как настроить Redisson, понять, как он работает, и изучить некоторые объекты и сервисы Redisson.

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

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

<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.1</version>
</dependency>

Последнюю версию этой зависимости можно найти здесь .

3. Конфигурация

Прежде чем мы начнем, мы должны убедиться, что у нас установлена и запущена последняя версия Redis. Если у вас нет Redis и вы используете Linux или Macintosh, вы можете следовать приведенной здесь информации, чтобы настроить его. Если вы пользователь Windows, вы можете настроить Redis, используя этот неофициальный порт .

Нам нужно настроить Redisson для подключения к Redis. Redisson поддерживает подключения к следующим конфигурациям Redis:

  • Один узел
  • Мастер с подчиненными узлами
  • Сторожевые узлы
  • Кластерные узлы
  • Реплицированные узлы

Redisson поддерживает кластер Amazon Web Services (AWS) ElastiCache и кэш Azure Redis для кластеризованных и реплицированных узлов.

Давайте подключимся к экземпляру Redis с одним узлом. Этот экземпляр работает локально на порту по умолчанию 6379:

RedissonClient client = Redisson.create();

Вы можете передавать различные конфигурации в метод создания объекта Redisson . Это могут быть конфигурации для подключения к другому порту или, может быть, для подключения к кластеру Redis. Эта конфигурация может быть в коде Java или загружена из внешнего файла конфигурации . `` ****

3.1. Конфигурация Java

Давайте настроим Redisson в коде Java:

Config config = new Config();
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379");

RedissonClient client = Redisson.create(config);

Мы указываем конфигурации Redisson в экземпляре объекта Config , а затем передаем его в метод create . Выше мы указали Redisson, что хотим подключиться к экземпляру Redis с одним узлом. Для этого мы использовали метод useSingleServer объекта Config . Это возвращает ссылку на объект SingleServerConfig .

Объект SingleServerConfig имеет настройки, которые Redisson использует для подключения к экземпляру Redis с одним узлом. Здесь мы используем его метод setAddress для настройки параметра адреса . Это устанавливает адрес узла, к которому мы подключаемся. Некоторые другие параметры включают retryAttempts , connectionTimeout и clientName . Эти параметры настраиваются с использованием соответствующих методов установки.

Мы можем настроить Redisson для различных конфигураций Redis аналогичным образом, используя следующие методы объекта Config :

  • useSingleServer — для экземпляра с одним узлом. Получить настройки одного узла здесь
  • useMasterSlaveServers — для мастера с подчиненными узлами. Получите настройки узла master-slave здесь
  • useSentinelServers — для дозорных узлов. Получите настройки дозорного узла здесь
  • useClusterServers — для кластерных узлов. Получите настройки кластерного узла здесь
  • useReplicatedServers — для реплицированных узлов. Получите реплицированные настройки узла здесь

3.2. Конфигурация файла

Redisson может загружать конфигурации из внешних файлов JSON или YAML:

Config config = Config.fromJSON(new File("singleNodeConfig.json"));  
RedissonClient client = Redisson.create(config);

Метод fromJSON объекта Config может загружать конфигурации из строки, файла, входного потока или URL-адреса. ``

Вот пример конфигурации в файле singleNodeConfig.json :

{
"singleServerConfig": {
"idleConnectionTimeout": 10000,
"connectTimeout": 10000,
"timeout": 3000,
"retryAttempts": 3,
"retryInterval": 1500,
"password": null,
"subscriptionsPerConnection": 5,
"clientName": null,
"address": "redis://127.0.0.1:6379",
"subscriptionConnectionMinimumIdleSize": 1,
"subscriptionConnectionPoolSize": 50,
"connectionMinimumIdleSize": 10,
"connectionPoolSize": 64,
"database": 0,
"dnsMonitoringInterval": 5000
},
"threads": 0,
"nettyThreads": 0,
"codec": null
}

Вот соответствующий файл конфигурации YAML:

singleServerConfig:
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
password: null
subscriptionsPerConnection: 5
clientName: null
address: "redis://127.0.0.1:6379"
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
connectionMinimumIdleSize: 10
connectionPoolSize: 64
database: 0
dnsMonitoringInterval: 5000
threads: 0
nettyThreads: 0
codec: !<org.redisson.codec.JsonJacksonCodec> {}

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

Чтобы сохранить конфигурацию Java в формате JSON или YAML, мы можем использовать методы toJSON или toYAML объекта Config :

Config config = new Config();
// ... we configure multiple settings here in Java
String jsonFormat = config.toJSON();
String yamlFormat = config.toYAML();

Теперь, когда мы знаем, как настроить Redisson, давайте посмотрим, как Redisson выполняет операции.

4. Операция

Redisson поддерживает синхронные, асинхронные и реактивные интерфейсы . Операции над этими интерфейсами потокобезопасны .

Все сущности (объекты, коллекции, блокировки и службы), созданные RedissonClient , имеют синхронные и асинхронные методы. Синхронные методы имеют асинхронные варианты . Эти методы обычно имеют то же имя метода, что и их синхронные варианты, с добавлением «Async». Давайте посмотрим на синхронный метод объекта RAtomicLong :

RedissonClient client = Redisson.create();
RAtomicLong myLong = client.getAtomicLong('myLong');

Асинхронный вариант синхронного метода compareAndSet будет таким:

RFuture<Boolean> isSet = myLong.compareAndSetAsync(6, 27);

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

isSet.handle((result, exception) -> {
// handle the result or exception here.
});

Для создания реактивных объектов нам потребуется использовать RedissonReactiveClient :

RedissonReactiveClient client = Redisson.createReactive();
RAtomicLongReactive myLong = client.getAtomicLong("myLong");

Publisher<Boolean> isSetPublisher = myLong.compareAndSet(5, 28);

Этот метод возвращает реактивные объекты на основе стандарта Reactive Streams для Java 9.

Давайте рассмотрим некоторые распределенные объекты, предоставляемые Redisson.

5. Объекты

Отдельный экземпляр объекта Redisson сериализуется и сохраняется на любом из доступных узлов Redis, поддерживающих Redisson . Эти объекты могут быть распределены в кластере по нескольким узлам, и доступ к ним может осуществляться одним приложением или несколькими приложениями/серверами.

Эти распределенные объекты соответствуют спецификациям пакета java.util.concurrent.atomic. Они поддерживают безблокировочные, потокобезопасные и атомарные операции над объектами, хранящимися в Redis . Согласованность данных между приложениями/серверами обеспечивается, поскольку значения не обновляются, пока другое приложение считывает объект.

Объекты Redisson привязаны к ключам Redis. Мы можем управлять этими ключами через интерфейс RKeys . И затем мы получаем доступ к нашим объектам Redisson, используя эти ключи.

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

Мы можем просто получить все ключи:

RKeys keys = client.getKeys();

В качестве альтернативы мы можем извлечь только имена:

Iterable<String> allKeys = keys.getKeys();

И, наконец, мы можем получить ключи, соответствующие шаблону:

Iterable<String> keysByPattern = keys.getKeysByPattern('key*')

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

Распределенные объекты, предоставляемые Redisson, включают:

  • ObjectHolder
  • BinaryStreamHolder
  • GeospatialHolder
  • Битсет
  • AtomicLong
  • АтомныйДабл
  • Тема
  • БлумФильтр
  • Гиперлоглог

Давайте рассмотрим три из этих объектов: ObjectHolder, AtomicLong и Topic.

5.1. Держатель объекта

Представленный классом RBucket , этот объект может содержать любой тип объекта. Этот объект имеет максимальный размер 512 МБ:

RBucket<Ledger> bucket = client.getBucket("ledger");
bucket.set(new Ledger());
Ledger ledger = bucket.get();

Объект RBucket может выполнять атомарные операции, такие как compareAndSet и getAndSet , с объектами, которые он содержит.

5.2. AtomicLong

Представленный классом RAtomicLong , этот объект очень похож на класс java.util.concurrent.atomic.AtomicLong и представляет длинное значение, которое может быть обновлено атомарно:

RAtomicLong atomicLong = client.getAtomicLong("myAtomicLong");
atomicLong.set(5);
atomicLong.incrementAndGet();

5.3. Тема

Объект Topic поддерживает механизм «публикации и подписки» Redis. Чтобы прослушать опубликованные сообщения:

RTopic subscribeTopic = client.getTopic("foreach");
subscribeTopic.addListener(CustomMessage.class,
(channel, customMessage) -> future.complete(customMessage.getMessage()));

Выше Тема зарегистрирована для прослушивания сообщений с канала «foreach». Затем мы добавляем прослушиватель в тему для обработки входящих сообщений из этого канала. Мы можем добавить несколько слушателей на канал.

Публикуем сообщения на канал «foreach»:

RTopic publishTopic = client.getTopic("foreach");
long clientsReceivedMessage
= publishTopic.publish(new CustomMessage("This is a message"));

Это может быть опубликовано из другого приложения или сервера. Объект CustomMessage будет получен прослушивателем и обработан, как определено в методе onMessage .

Подробнее о других объектах Redisson мы можем узнать здесь .

6. Коллекции

Мы работаем с коллекциями Redisson так же, как с объектами.

Распределенные коллекции, предоставляемые Redisson, включают:

  • карта
  • Мультикарта
  • Установлен
  • Сортированный набор
  • Сортированный набор
  • Лекс СортедСет
  • Список
  • Очередь
  • Дек
  • Блокировка очереди
  • ограниченная блокировка очереди
  • BlockingDeque
  • БлокировкаFairQueue
  • Отложенная очередь
  • PriorityQueue
  • PriorityDeque

Давайте рассмотрим три из этих коллекций: Map, Set и List.

6.1. карта

Карты на основе Redisson реализуют интерфейсы java.util.concurrent.ConcurrentMap и java.util.Map . У Redisson есть четыре реализации карт. Это RMap , RMapCache , RLocalCachedMap и RClusteredMap .

Давайте создадим карту с Redisson:

RMap<String, Ledger> map = client.getMap("ledger");
Ledger newLedger = map.put("123", new Ledger());map

RMapCache поддерживает вытеснение записи карты. RLocalCachedMap позволяет локально кэшировать записи карты . RClusteredMap позволяет разделить данные из одной карты между главными узлами кластера Redis.

Подробнее о картах Redisson мы можем узнать здесь .

6.2. Установлен

Set на основе Redisson реализует интерфейс java.util.Set .

У Redisson есть три реализации Set : RSet , RSetCache и RClusteredSet с функциональностью, аналогичной их аналогам карты.

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

RSet<Ledger> ledgerSet = client.getSet("ledgerSet");
ledgerSet.add(new Ledger());

Подробнее о наборах Redisson мы можем узнать здесь .

6.3. Список

Списки на основе Redisson реализуют интерфейс java.util.List .

Давайте создадим список с Redisson:

RList<Ledger> ledgerList = client.getList("ledgerList");
ledgerList.add(new Ledger());

Узнать больше о других коллекциях Redisson можно здесь .

7. Блокировки и синхронизаторы

Распределенные блокировки Redisson позволяют синхронизировать потоки между приложениями/серверами. Список замков и синхронизаторов Redisson включает:

  • Замок
  • Фэйрлок
  • Мультилок
  • ReadWriteLock
  • семафор
  • PermitExpirableSemaphore
  • Защелка

Давайте взглянем на Lock и MultiLock.

7.1. Замок

Блокировка Redisson реализует интерфейс java.util.concurrent.locks.Lock .

Давайте реализуем блокировку, представленную классом RLock :

RLock lock = client.getLock("lock");
lock.lock();
// perform some long operations...
lock.unlock();

7.2. Мультилок

RedissonMultiLock группирует несколько объектов RLock и обрабатывает их как одну блокировку: ``

RLock lock1 = clientInstance1.getLock("lock1");
RLock lock2 = clientInstance2.getLock("lock2");
RLock lock3 = clientInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
lock.lock();
// perform long running operation...
lock.unlock();

Мы можем узнать больше о других замках здесь .

8. Услуги

Redisson предоставляет 4 типа распределенных сервисов. Это: удаленная служба , служба живых объектов , служба исполнителя и служба исполнителя по расписанию . Давайте посмотрим на удаленную службу и службу живых объектов.

8.1. Удаленное обслуживание

Эта служба обеспечивает удаленный вызов методов Java с помощью Redis . Удаленная служба Redisson состоит из реализации на стороне сервера (рабочий экземпляр) и реализации на стороне клиента. Реализация на стороне сервера выполняет удаленный метод, вызванный клиентом. Вызовы из удаленной службы могут быть синхронными или асинхронными.

На стороне сервера регистрируется интерфейс для удаленного вызова:

RRemoteService remoteService = client.getRemoteService();
LedgerServiceImpl ledgerServiceImpl = new LedgerServiceImpl();

remoteService.register(LedgerServiceInterface.class, ledgerServiceImpl);

Клиентская сторона вызывает метод зарегистрированного удаленного интерфейса:

RRemoteService remoteService = client.getRemoteService();
LedgerServiceInterface ledgerService
= remoteService.get(LedgerServiceInterface.class);

List<String> entries = ledgerService.getEntries(10);

Мы можем узнать больше об удаленных услугах здесь .

8.2. Служба живых объектов

Redisson Live Objects расширяет концепцию стандартных объектов Java, доступ к которым возможен только из одной виртуальной машины JVM, до расширенных объектов Java, которые могут совместно использоваться различными виртуальными машинами JVM на разных машинах . Это достигается путем сопоставления полей объекта с хешем Redis. Это сопоставление выполняется через прокси-класс, созданный во время выполнения. Методы получения и установки полей сопоставляются с командами Redis hget/hset.

Redisson Live Objects поддерживает доступ к атомарным полям благодаря однопоточной природе Redis.

Создать живой объект просто:

@REntity
public class LedgerLiveObject {
@RId
private String name;

// getters and setters...
}

Мы аннотируем наш класс с помощью @REntity и уникальное или идентифицирующее поле с помощью @RId . Как только мы это сделали, мы можем использовать наш Live Object в нашем приложении:

RLiveObjectService service = client.getLiveObjectService();

LedgerLiveObject ledger = new LedgerLiveObject();
ledger.setName("ledger1");

ledger = service.persist(ledger);

Мы создаем наш Live Object как стандартные объекты Java, используя ключевое слово new . Затем мы используем экземпляр RLiveObjectService для сохранения объекта в Redis с помощью его метода persist .

Если объект ранее был сохранен в Redis, мы можем получить объект:

LedgerLiveObject returnLedger
= service.get(LedgerLiveObject.class, "ledger1");

Мы используем RLiveObjectService , чтобы получить наш Live Object, используя поле с аннотацией @RId .

Здесь мы можем найти больше о Redisson Live Objects, а другие услуги Redisson описаны здесь.

9. Конвейерная обработка

Redisson поддерживает конвейерную обработку. Несколько операций могут быть объединены в одну атомарную операцию . Этому способствует класс RBatch . Несколько команд объединяются с экземпляром объекта RBatch перед их выполнением:

RBatch batch = client.createBatch();
batch.getMap("ledgerMap").fastPutAsync("1", "2");
batch.getMap("ledgerMap").putAsync("2", "5");

BatchResult<?> batchResult = batch.execute();

10. Сценарии

Redisson поддерживает сценарии LUA. Мы можем выполнять сценарии LUA против Redis :

client.getBucket("foo").set("bar");
String result = client.getScript().eval(Mode.READ_ONLY,
"return redis.call('get', 'foo')", RScript.ReturnType.VALUE);

11. Низкоуровневый клиент

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

RedisClientConfig redisClientConfig = new RedisClientConfig();
redisClientConfig.setAddress("localhost", 6379);

RedisClient client = RedisClient.create(redisClientConfig);

RedisConnection conn = client.connect();
conn.sync(StringCodec.INSTANCE, RedisCommands.SET, "test", 0);

conn.closeAsync();
client.shutdown();

Клиент низкого уровня также поддерживает асинхронные операции.

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

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

Redisson также обеспечивает интеграцию с другими платформами , такими как JCache API, Spring Cache, Hibernate Cache и Spring Sessions. Подробнее о его интеграции с другими фреймворками мы можем узнать здесь .

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