Хабрахабр

[Перевод] Введение в 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 не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть