Введение в Redis с использованием Spring Boot

от автора

Перевод статьи подготовлен специально для студентов курса «Разработчик на Spring Framework».


В этой статье мы рассмотрим основы использования Redis через Spring Boot с помощью библиотеки Spring Data Redis.

Мы создадим приложение, которое демонстрирует, как выполнять CRUD-операции через веб-интерфейс. Исходный код этого проекта доступен на GitHub.

Что такое Redis?

Redis — это хранилище данных с открытым исходным кодом, для структур данных «ключ-значение», которое можно использовать в качестве базы данных, кэша и брокера сообщений. С точки зрения реализации, хранилища «ключ-значение» являются одними из самых больших и старых представителей в мире NoSQL. Redis поддерживает такие структуры данных, как строки, хэши, списки, множества и отсортированные множества с запросами диапазонов.

Фреймворк Spring Data Redis дает возможность простого написания Spring-приложений, которые используют хранилище Redis, предоставляя удобную абстракцию хранилища данных.

Настройка сервера Redis

Сервер доступен бесплатно здесь.
Если вы используете Mac, вы можете установить его с помощью homebrew:

brew install redis

Затем запустите сервер:

mikes-MacBook-Air:~ mike$ redis-server 10699:C 23 Nov 08:35:58.306 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 10699:C 23 Nov 08:35:58.307 # Redis version=4.0.2, bits=64, commit=00000000, modified=0, pid=10699, just started 10699:C 23 Nov 08:35:58.307 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 10699:M 23 Nov 08:35:58.309 * Increased maximum number of open files to 10032 (it was originally set to 256).                 _._                                                              _.-``__ ''-._                                                    _.-``    `.  `_.  ''-._           Redis 4.0.2 (00000000/0) 64 bit   .-`` .-```.  ```\/    _.,_ ''-._                                     (    '      ,       .-`  | `,    )     Running in standalone mode  |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379  |    `-._   `._    /     _.-'    |     PID: 10699   `-._    `-._  `-./  _.-'    _.-'                                    |`-._`-._    `-.__.-'    _.-'_.-'|                                    |    `-._`-._        _.-'_.-'    |           http://redis.io           `-._    `-._`-.__.-'_.-'    _.-'                                     |`-._`-._    `-.__.-'    _.-'_.-'|                                    |    `-._`-._        _.-'_.-'    |                                     `-._    `-._`-.__.-'_.-'    _.-'                                          `-._    `-.__.-'    _.-'                                                 `-._        _.-'                                                          `-.__.-'                                                10699:M 23 Nov 08:35:58.312 # Server initialized 10699:M 23 Nov 08:35:58.312 * Ready to accept connections

Maven зависимости

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

<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-web</artifactId> </dependency> 

Конфигурация Redis

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

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

Начнем с определений конфигурационных бинов:

@Bean JedisConnectionFactory jedisConnectionFactory() {     return new JedisConnectionFactory(); } @Bean public RedisTemplate<String, Object> redisTemplate() {     final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();     template.setConnectionFactory(jedisConnectionFactory());     template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class));     return template; } 

JedisConnectionFactory представлен как bean-компонент, так что мы можем создать RedisTemplate для запроса данных.

Издатель сообщений

Следуя принципам SOLID, мы создаем интерфейс MessagePublisher:

public interface MessagePublisher {     void publish(final String message); }

Мы реализуем интерфейс MessagePublisher с использованием высокоуровневого RedisTemplate для публикации сообщения, так как RedisTemplate позволяет передавать произвольные объекты в виде сообщений:

@Service public class MessagePublisherImpl implements MessagePublisher {     @Autowired     private RedisTemplate<String, Object> redisTemplate;     @Autowired     private ChannelTopic topic;     public MessagePublisherImpl() {     }     public MessagePublisherImpl(final RedisTemplate<String, Object> redisTemplate, final ChannelTopic topic) {         this.redisTemplate = redisTemplate;         this.topic = topic;     }     public void publish(final String message) {         redisTemplate.convertAndSend(topic.getTopic(), message);     } }

Мы также определяем это как bean-компонент в RedisConfig:

@Bean MessagePublisher redisPublisher() {     return new MessagePublisherImpl(redisTemplate(), topic()); } 

Получатель сообщений

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

@Service public class MessageSubscriber implements MessageListener {     public static List<String> messageList = new ArrayList<String>();     public void onMessage(final Message message, final byte[] pattern) {         messageList.add(message.toString());         System.out.println("Message received: " + new String(message.getBody()));     } }

Также этот класс необходимо зарегистрировать как bean-компонент в RedisConfig:

@Bean MessageListenerAdapter messageListener() {     return new MessageListenerAdapter(new MessageSubscriber()); }

RedisRepository

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

Модель

Для этого примера мы определяем модель Movie с двумя полями:

private String id; private String name; //standard getters and setters

Интерфейс репозитория

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

Часто нет необходимости писать реализацию интерфейса репозитория с проектами Spring Data. Мы просто взаимодействуем с интерфейсом. Spring Data JPA предоставляет многочисленные интерфейсы репозитория, которые могут быть расширены для получения таких функций, как CRUD операции, производные запросы и разбиение на страницы.

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

public interface RedisRepository {     Map<Object, Object> findAllMovies();     void add(Movie movie);     void delete(String id);     Movie findMovie(String id); }

Реализация репозитория

Класс использует redisTemplate, определенный в классе конфигурации RedisConfig.

Мы используем HashOperations, который предлагает Spring Data Redis:

@Repository public class RedisRepositoryImpl implements RedisRepository {     private static final String KEY = "Movie";     private RedisTemplate<String, Object> redisTemplate;     private HashOperations hashOperations;     @Autowired     public RedisRepositoryImpl(RedisTemplate<String, Object> redisTemplate){         this.redisTemplate = redisTemplate;     }     @PostConstruct     private void init(){         hashOperations = redisTemplate.opsForHash();     }     public void add(final Movie movie) {         hashOperations.put(KEY, movie.getId(), movie.getName());     }     public void delete(final String id) {         hashOperations.delete(KEY, id);     }     public Movie findMovie(final String id){         return (Movie) hashOperations.get(KEY, id);     }     public Map<Object, Object> findAllMovies(){         return hashOperations.entries(KEY);     } }

Давайте обратим внимание на метод init(). В этом методе мы используем функцию с именем opsForHash(), она возвращает операции выполняемые с хеш-значениями, привязанными к данному ключу. Затем мы используем hashOps, который был определен в init(), для всех наших CRUD операций.

Веб-интерфейс

В этом разделе мы рассмотрим добавление возможностей CRUD операций Redis в веб-интерфейс.

Добавление фильма

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

Давайте добавим форму в HTML-документ и назначим соответствующие имена и идентификаторы:

<form id="addForm"> <div class="form-group">                     <label for="keyInput">Movie ID (key)</label>                     <input name="keyInput" id="keyInput" class="form-control"/>                 </div> <div class="form-group">                     <label for="valueInput">Movie Name (field of Movie object value)</label>                     <input name="valueInput" id="valueInput" class="form-control"/>                 </div>                 <button class="btn btn-default" id="addButton">Add</button>  </form>

Теперь мы используем JavaScript для сохранения значений при отправке формы:

$(document).ready(function() {     var keyInput = $('#keyInput'),         valueInput = $('#valueInput');     refreshTable();     $('#addForm').on('submit', function(event) {         var data = {             key: keyInput.val(),             value: valueInput.val()         };         $.post('/add', data, function() {             refreshTable();             keyInput.val('');             valueInput.val('');             keyInput.focus();         });         event.preventDefault();     });     keyInput.focus(); }); 

Задаем параметры @RequestMapping для POST запроса, запрашиваем ключ и значение, создаем объект Movie и сохраняем его в хранилище:

@RequestMapping(value = "/add", method = RequestMethod.POST) public ResponseEntity<String> add(     @RequestParam String key,     @RequestParam String value) {     Movie movie = new Movie(key, value);     redisRepository.add(movie);     return new ResponseEntity<>(HttpStatus.OK); } 

Просмотр контента

Как только объект Movie добавлен, мы обновляем таблицу для отображения новых значений. В блоке JavaScript кода мы вызывали функцию refreshTable(). Она выполняет GET запрос для получения текущих данных в хранилище:

function refreshTable() {     $.get('/values', function(data) {         var attr,             mainTable = $('#mainTable tbody');         mainTable.empty();         for (attr in data) {             if (data.hasOwnProperty(attr)) {                 mainTable.append(row(attr, data[attr]));             }         }     }); }

GET запрос обрабатывается методом findAll(), который извлекает все объекты Movie, хранящиеся в хранилище, а затем преобразует тип данных из Map <Object, Object> в Map <String, String>:

@RequestMapping("/values") public @ResponseBody Map<String, String> findAll() {     Map<Object, Object> aa = redisRepository.findAllMovies();     Map<String, String> map = new HashMap<String, String>();     for(Map.Entry<Object, Object> entry : aa.entrySet()){         String key = (String) entry.getKey();         map.put(key, aa.get(key).toString());     }     return map; }

Удаление фильма

Напишем скрипт для выполнения POST запроса по пути /delete, обновления таблицы и переключения фокуса клавиатуры для удобного ввода:

function deleteKey(key) {     $.post('/delete', {key: key}, function() {         refreshTable();         $('#keyInput').focus();     }); }

Мы запрашиваем ключ и удаляем объект в redisRepository на основе этого ключа:

@RequestMapping(value = "/delete", method = RequestMethod.POST) public ResponseEntity<String> delete(@RequestParam String key) {     redisRepository.delete(key);     return new ResponseEntity<>(HttpStatus.OK); }

Демо

Здесь мы добавили два фильма:

И один фильм удалили:

Заключение

В этом руководстве мы рассмотрели Spring Data Redis и один из способов подключения его к веб-приложению для выполнения CRUD-операций.

Исходный код для примера приложения находится на GitHub.

На этом все. Традиционно ждем ваши комментарии.


ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/463365/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *