[Перевод] Введение в 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
для приложения, с которым будем работать:
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
Конфигурация Redis
Нам нужно связать наше приложение с сервером Redis. Чтобы установить соединение, мы используем Jedis, клиентскую реализацию Redis.
Конфигурация
Начнем с определений конфигурационных бинов:
@Bean
JedisConnectionFactory jedisConnectionFactory() {
return new JedisConnectionFactory();
}
@Bean
public RedisTemplate redisTemplate() {
final RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(jedisConnectionFactory());
template.setValueSerializer(new GenericToStringSerializer
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 redisTemplate;
@Autowired
private ChannelTopic topic;
public MessagePublisherImpl() {
}
public MessagePublisherImpl(final RedisTemplate 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 messageList = new ArrayList();
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
Реализация репозитория
Класс использует redisTemplate
, определенный в классе конфигурации RedisConfig
.
Мы используем HashOperations
, который предлагает Spring Data Redis:
@Repository
public class RedisRepositoryImpl implements RedisRepository {
private static final String KEY = "Movie";
private RedisTemplate redisTemplate;
private HashOperations hashOperations;
@Autowired
public RedisRepositoryImpl(RedisTemplate 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
Давайте обратим внимание на метод init()
. В этом методе мы используем функцию с именем opsForHash()
, она возвращает операции выполняемые с хеш-значениями, привязанными к данному ключу. Затем мы используем hashOps
, который был определен в init()
, для всех наших CRUD операций.
Веб-интерфейс
В этом разделе мы рассмотрим добавление возможностей CRUD операций Redis в веб-интерфейс.
Добавление фильма
Мы хотим иметь возможность добавить фильм через веб-страницу. Ключ — это идентификатор фильма, а значение — фактический объект. Однако позже мы вернемся к этому, поэтому в качестве значения отображается только название фильма.
Давайте добавим форму в HTML-документ и назначим соответствующие имена и идентификаторы:
Теперь мы используем 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 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
в Map
:
@RequestMapping("/values")
public @ResponseBody Map findAll() {
Map
Удаление фильма
Напишем скрипт для выполнения POST запроса по пути /delete
, обновления таблицы и переключения фокуса клавиатуры для удобного ввода:
function deleteKey(key) {
$.post('/delete', {key: key}, function() {
refreshTable();
$('#keyInput').focus();
});
}
Мы запрашиваем ключ и удаляем объект в redisRepository
на основе этого ключа:
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public ResponseEntity delete(@RequestParam String key) {
redisRepository.delete(key);
return new ResponseEntity<>(HttpStatus.OK);
}
Демо
Здесь мы добавили два фильма:
И один фильм удалили:
Заключение
В этом руководстве мы рассмотрели Spring Data Redis и один из способов подключения его к веб-приложению для выполнения CRUD-операций.
Исходный код для примера приложения находится на GitHub.
На этом все. Традиционно ждем ваши комментарии.