Вступление
Redis - это хранилище данных в памяти, которое можно использовать как базу данных NoSQL, кеш или как типичный брокер сообщений. Он написан на ANSI C, который компилируется в значительно эффективный машинный код, а его способность хранить данные в виде пар ключ-значение делает кэширование в памяти привлекательным вариантом использования Redis, помимо сохранения данных на диске.
В этой статье мы будем использовать конвейерную обработку, чтобы приложение Spring Boot могло отправлять несколько запросов на сервер Redis неблокирующим способом.
Пример использования конвейерной обработки в Redis
Redis основан на архитектуре клиент / сервер (запрос / ответ). В этих архитектурах клиент обычно отправляет запрос или запрос на сервер и ожидает ответа. Обычно это делается блокирующим способом, так что новый запрос не может быть отправлен до тех пор, пока не будет отправлен ответ на последний:
Client: <command 1>
Server: Response for <command 1>
Client: <command 2>
Server: Response for <command 2>
Client: <command 3>
Server: Response for <command 3>
Это может привести к значительной неэффективности с большой задержкой между получением команд и их обработкой.
Конвейерная обработка позволяет нам отправлять несколько команд как одну операцию клиента, не дожидаясь ответа от сервера между каждой командой. Затем вместо этого все ответы читаются вместе:
Client: <command 1>
Client: <command 2>
Client: <command 3>
Server: Response for <command 1>
Server: Response for <command 2>
Server: Response for <command 3>
Поскольку клиент не ожидает ответа сервера перед отправкой другой команды, время ожидания уменьшается, что, в свою очередь, улучшает производительность приложения.
Примечание . Команды здесь помещаются в очередь. Эта очередь должна оставаться разумного размера. Если вы имеете дело с десятками тысяч команд, лучше отправлять и обрабатывать их партиями, чтобы преимущества конвейерной обработки не стали излишними.
Выполнение
Давайте продолжим и создадим небольшое приложение Spring Boot, которое работает с Redis и передает несколько команд по конвейеру. Это стало проще с помощью проекта Spring Data Redis .
Настройка Spring Boot
Самый простой способ начать с пустого приложения Spring Boot - использовать Spring Initializr :
{.ezlazyload}
В качестве альтернативы вы также можете использовать Spring Boot CLI для начальной загрузки приложения:
$ spring init --dependencies=spring-boot-starter-data-redis redis-spring-boot-demo
Мы начинаем с spring-boot-starter-data-redis
поскольку она включает в
себя spring-data-redis
, spring-boot-starter
и lettuce-core
.
Если у вас уже есть приложение Maven / Spring, добавьте зависимость в
свой файл pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${version}</version>
</dependency>
Или, если вы используете Gradle:
compile group: 'org.springframework.data', name: 'spring-data-redis', version: '${version}'
Мы также будем использовать Jedis в качестве клиента подключения вместо Lettuce:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${version}</version>
</dependency>
Конфигурация Redis
Мы разместим Redis на Scalegrid , который предоставляет бесплатную пробную учетную запись для размещения экземпляра сервера Redis. Кроме того, вы можете загрузить сервер и разместить его на своем компьютере в Linux и MacOS. Windows требует небольшого взлома, и ее сложно настроить.
Давайте JedisConnectionFactory
чтобы наше приложение могло
подключаться к экземпляру сервера Redis. В вашем @Configuration
аннотируйте соответствующий @Bean
:
@Configuration
public class Config {
@Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("<server-hostname-here>");
jedisConnectionFactory.setPort(6379);
jedisConnectionFactory.setPassword("<server-password-here>");
jedisConnectionFactory.afterPropertiesSet();
return jedisConnectionFactory;
}
}
RedisTemplate
- это входной класс, предоставляемый Spring Data, через
который мы взаимодействуем с сервером Redis.
Мы передадим ему нашу настроенную фабрику соединений:
@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setDefaultSerializer(RedisSerializer.string());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
Мы установили сериализатор по умолчанию для хранения ключей и значений
как String
. В качестве альтернативы вы можете определить свой
собственный сериализатор.
Конвейерная обработка с использованием RedisTemplate
Давайте добавим несколько элементов из списка на сервер Redis. Мы
сделаем это без конвейерной обработки, используя RedisTemplate
. В
частности, мы будем использовать ListOperations
, полученный из
opsForList()
:
List<String> values = Arrays.asList("value-1", "value-2", "value-3", "value-4", "value-5");
redisTemplate.opsForList().leftPushAll("pipeline-list", values);
Выполнение этого кода приведет к:
{.ezlazyload}
Теперь давайте удалим это. Представляя, что это может быть дорогостоящая
операция, мы будем конвейерно rPop()
чтобы они отправлялись вместе и
чтобы результаты синхронизировались после удаления всех элементов. Затем
мы получим эти результаты обратно. Для конвейерных команд мы используем
метод executedPipeline()
.
Он принимает RedisCallback
или SessionCallback
которые мы ему
предоставляем. Метод executedPipeline()
возвращает результаты, которые
мы затем можем зафиксировать и просмотреть. Если в этом нет
необходимости и вы просто хотите выполнить команды, вы можете
использовать метод execute()
и вместо этого true
в качестве
pipeline
List<Object> results = redisTemplate.executePipelined(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
for(int i = 0; i < 5; i++) {
connection.rPop("pipeline-list".getBytes());
}
return null;
}
});
return results;
Метод executePipelined()
принял new RedisCallback()
, в котором мы
используем метод doInRedis()
чтобы указать, что мы хотим сделать.
В частности, мы запустили метод rPop()
5 раз, удалив 5 элементов
списка, которые мы вставили заранее.
После выполнения всех пяти этих команд элементы удаляются из списка и
отправляются обратно - результаты упаковываются в список results
{.ezlazyload}
Заключение
Самый популярный вариант использования Redis - это хранилище кешей. Однако его также можно использовать в качестве базы данных или посредника сообщений.
Redis позволяет нам повысить производительность приложений за счет минимизации обращений к уровню базы данных. Его поддержка конвейерной обработки позволяет отправлять на сервер несколько команд за одну операцию записи, тем самым сокращая время приема и передачи на сервер и обратно.
В этой статье мы конвейеризовали несколько команд с помощью
RedisTemplate
API и проверили результаты.