Хабрахабр

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

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

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

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

Что такое 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 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.

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

Показать больше

Похожие публикации

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

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

Кнопка «Наверх»