[Программирование, Java, Промышленное программирование, Тестирование веб-сервисов] Как подружить Redis Cluster c Testcontainers?
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В 26-м выпуске NP-полного подкаста я рассказывал, что начал переводить один из своих сервисов из Redis Sentinel на Redis Cluster. На этой неделе я захотел потестировать данный код, и, конечно же, выбрал Testcontainers для этого. К сожалению, Redis Cluster в тестовых контейнерах не завелся из коробки, и мне пришлось вставить несколько костылей. О них и пойдет речь далее.
Вводные
Сначала я бы хотел описать все вводные, а потом рассказать про костыли. Мой проект построен на Spring Boot. Для взаимодействия с редисом используется Lettuce клиент. Для тестирования — testcontainers-java с JUnit. Версия обоих редисов — 6. В общем, всё типичное, нет ничего особенного с точки зрения стека.
Если кто-то еще не знаком с testcontainers, то пара слов о них. Это библиотека для интеграционного тестирования. Она построена на другой библиотеке — https://github.com/docker-java/docker-java. Тестконтейнеры, по сути говоря, помогают быстро и просто запускать контейнеры с разными зависимостями в ваших интеграционных тестах. Обычно это базы данных, очереди и другие сложные системы. Некоторые люди используют testcontainers и для запуска своих сервисов, от которых зависит тестируемое приложение (чтобы тестировать микросервисное взаимодействие).
Про Redis Cluster
Redis Cluster — это одна из нескольких реализаций распределнного режима Редиса. К сожалению, в Редисе нет единого правильного способа, как масштабировать базу. Есть Sentinel, есть Redis Cluster, а еще ребята активно разрабатывают RedisRaft — распредеделенный редис на базе протокола консенсуса Raft (у них там своя реализация, которая, как они сами заявляют, не совсем каноничный Рафт, но конкретно для Redis — самое то).
В целом, про Redis Cluster есть две замечательных статьи на официальном сайте — https://redis.io/topics/cluster-tutorial и https://redis.io/topics/cluster-spec. Большинство деталей описано там.
Для использования Redis Cluster в testcontainers важно знать несколько вещей из документации. Во-первых, Redis Cluster использует gossip протокол — поэтому каждый узел кластера имеет TCP-соединение со всеми другими узлами. Поэтому, между нодами должна быть сетевая связность, даже в тестах.
Вторая важная штука, которую надо знать при тестировании — это наличие в Redis Cluster bootstrap узлов для конфигурации. То есть, вы в настройках можете задать лишь подмножество узлов, которые будут использоваться для старта приложения. В последствие, Redis клиент сам получит Топологию кластера через взаимодействие с Редисом. Исходя из этого, получается вторая особенность — тестируемое приложение должно иметь сетевую связность с теми Redis URI, которые будут аннонсированы со стороны редис кластера (кстати, эти адреса можно сконфигурировать через cluster-announce-port и cluster-announce-ip).
Про костыли с Redis Cluster и testcontainers
Для тестирования я выбрал довольно популярный docker-образ — https://github.com/Grokzen/docker-redis-cluster. Он не подходит для продакшена, но очень прост в использовании в тестах. Особенность этого образа — все Редисы (а их 6 штук, по умолчанию — 3 мастера и 3 слейва) будут подняты в рамках одного контейнера. Поэтому, мы автоматически получаем сетевую связность между узлами кластера из коробки. Осталось решить вторую из двух проблем, связанную с получением приложением топологии кластера.
Я не хотел собирать свой docker-образ, а выбранный мной image не предоставляет возможности задавать настройки cluster-announce-port и cluster-announce-ip. Поэтому, если ничего не делать дополнительно, при запуске тестов вы увидите примерно такие ошибки:
Unable to connect to [172.17.0.3/<unresolved>:7003]: connection timed out: /172.17.0.3:7003
Ошибка означает, что мы со стороны приложения пытаеся приконнектится к Узлу редис кластера, используя IP докер контейнера и внутренний порт (порт 7003 используется данным узлом, но наружу он отображается на какой-то случайный порт, который мы и должны использовать в нашем приложении; внутренний порт, по понятным причинам, не доступен из вне). Что касается данного IP-адреса — он доступен для приложения, если это Linux, и он не доступен для приложения, если это MacOs/Windows (из-за особенностей реализации докера на этих ОС).
Решение проблемы (а-ка костыль) я собрал по частичкам из разных статей. А давайте сделаем NAT RedisURI на стороне приложения. Ведь это нужно именно для тестов, и тут не так страшно вставлять такой ужас. Решение, на самом деле, состоит из пары строк (огромное спасибо Спрингу и Lettuce, где можно сконфигурировать практически всё, только и успевай, как переопределять бины).
public SocketAddress resolve(RedisURI redisURI) {
Integer mappedPort = redisClusterNatPortMapping.get(redisURI.getPort());
if (mappedPort != null) {
SocketAddress socketAddress = redisClusterSocketAddresses.get(mappedPort);
if (socketAddress != null) {
return socketAddress;
}
redisURI.setPort(mappedPort);
}
redisURI.setHost(DockerClientFactory.instance().dockerHostIpAddress());
SocketAddress socketAddress = super.resolve(redisURI);
redisClusterSocketAddresses.putIfAbsent(redisURI.getPort(), socketAddress);
return socketAddress;
}
Полный код выложен на гитхаб — https://github.com/Hixon10/spring-redis-cluster-testcontainers.
Идея кода супер простая. Будем хранить две Map. В первой — маппинг между внутренними портами редиса (7000..7005) и теми, что доступны для приложения (они могут быть чем-то типа 51343, 51344 и тд). Во-второй — внешние порты (типа, 51343) и SocketAddress, полученный для них. Теперь, когда мы получаем от Редиса при обновлении топологии что-то типа 172.17.0.3:7003, мы сможем легко найти нужный внешний порт, по которому сможем найти SocketAddress и переиспользовать его. То есть, с портами проблема решена. А что с IP?
С IP-адресом всё просто. Тут нам на помощь приходят Тест контейнеры в которых есть утилитный метод — DockerClientFactory.instance().dockerHostIpAddress(). Для MacOs/Windows он будет отдавать localhost, а для linux — IP-адрес контейнера.
Выводы
Программирование — это супер интересно, но это вы и без меня знали. А ещё, порой приходится вспомить, что было на первой лекции по сетям в универе, чтобы написать на своей любимой джавке пару новых интеграционных тестов. Приятно, когда знания из института пригождаются в самый неожиданный момент.
===========
Источник:
habr.com
===========
Похожие новости:
- [JavaScript, ReactJS] За что я не люблю Redux
- [Программирование] Локальное время и дата рождения или зачем UTC
- [Программирование, Венчурные инвестиции, Развитие стартапа, Образование за рубежом, Бизнес-модели] Перевод Курса по стартапам и бизнесу от Стэнфордского Университета. Лекция №2. Команда и реализация стартапа (перевод)
- [CSS, JavaScript, ReactJS, TypeScript] Темизация. История, причины, реализация
- [Программирование микроконтроллеров] Программируемое реле easyE4
- [Разработка веб-сайтов, Программирование, Haskell, Функциональное программирование] Создаем веб-приложение на Haskell с использованием Reflex. Часть 4
- [Open source, Программирование, Системное программирование, Компиляторы, Rust] Rust 1.53.0: IntoIterator для массивов, "|" в шаблонах, Unicode-идентификаторы, поддержка имени HEAD-ветки в Cargo (перевод)
- [Программирование, Геоинформационные сервисы, Математика, Научно-популярное, Физика] Оцениваем открытые и коммерческие цифровые модели рельефа
- [Python, Программирование, Машинное обучение] Автоматизация машинного обучения
- [Java] Посмотрим на rx.Single в RxJava?
Теги для поиска: #_programmirovanie (Программирование), #_java, #_promyshlennoe_programmirovanie (Промышленное программирование), #_testirovanie_vebservisov (Тестирование веб-сервисов), #_java, #_redis, #_spring, #_testcontainers, #_programmirovanie (
Программирование
), #_java, #_promyshlennoe_programmirovanie (
Промышленное программирование
), #_testirovanie_vebservisov (
Тестирование веб-сервисов
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:38
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В 26-м выпуске NP-полного подкаста я рассказывал, что начал переводить один из своих сервисов из Redis Sentinel на Redis Cluster. На этой неделе я захотел потестировать данный код, и, конечно же, выбрал Testcontainers для этого. К сожалению, Redis Cluster в тестовых контейнерах не завелся из коробки, и мне пришлось вставить несколько костылей. О них и пойдет речь далее. Вводные Сначала я бы хотел описать все вводные, а потом рассказать про костыли. Мой проект построен на Spring Boot. Для взаимодействия с редисом используется Lettuce клиент. Для тестирования — testcontainers-java с JUnit. Версия обоих редисов — 6. В общем, всё типичное, нет ничего особенного с точки зрения стека. Если кто-то еще не знаком с testcontainers, то пара слов о них. Это библиотека для интеграционного тестирования. Она построена на другой библиотеке — https://github.com/docker-java/docker-java. Тестконтейнеры, по сути говоря, помогают быстро и просто запускать контейнеры с разными зависимостями в ваших интеграционных тестах. Обычно это базы данных, очереди и другие сложные системы. Некоторые люди используют testcontainers и для запуска своих сервисов, от которых зависит тестируемое приложение (чтобы тестировать микросервисное взаимодействие). Про Redis Cluster Redis Cluster — это одна из нескольких реализаций распределнного режима Редиса. К сожалению, в Редисе нет единого правильного способа, как масштабировать базу. Есть Sentinel, есть Redis Cluster, а еще ребята активно разрабатывают RedisRaft — распредеделенный редис на базе протокола консенсуса Raft (у них там своя реализация, которая, как они сами заявляют, не совсем каноничный Рафт, но конкретно для Redis — самое то). В целом, про Redis Cluster есть две замечательных статьи на официальном сайте — https://redis.io/topics/cluster-tutorial и https://redis.io/topics/cluster-spec. Большинство деталей описано там. Для использования Redis Cluster в testcontainers важно знать несколько вещей из документации. Во-первых, Redis Cluster использует gossip протокол — поэтому каждый узел кластера имеет TCP-соединение со всеми другими узлами. Поэтому, между нодами должна быть сетевая связность, даже в тестах. Вторая важная штука, которую надо знать при тестировании — это наличие в Redis Cluster bootstrap узлов для конфигурации. То есть, вы в настройках можете задать лишь подмножество узлов, которые будут использоваться для старта приложения. В последствие, Redis клиент сам получит Топологию кластера через взаимодействие с Редисом. Исходя из этого, получается вторая особенность — тестируемое приложение должно иметь сетевую связность с теми Redis URI, которые будут аннонсированы со стороны редис кластера (кстати, эти адреса можно сконфигурировать через cluster-announce-port и cluster-announce-ip). Про костыли с Redis Cluster и testcontainers Для тестирования я выбрал довольно популярный docker-образ — https://github.com/Grokzen/docker-redis-cluster. Он не подходит для продакшена, но очень прост в использовании в тестах. Особенность этого образа — все Редисы (а их 6 штук, по умолчанию — 3 мастера и 3 слейва) будут подняты в рамках одного контейнера. Поэтому, мы автоматически получаем сетевую связность между узлами кластера из коробки. Осталось решить вторую из двух проблем, связанную с получением приложением топологии кластера. Я не хотел собирать свой docker-образ, а выбранный мной image не предоставляет возможности задавать настройки cluster-announce-port и cluster-announce-ip. Поэтому, если ничего не делать дополнительно, при запуске тестов вы увидите примерно такие ошибки: Unable to connect to [172.17.0.3/<unresolved>:7003]: connection timed out: /172.17.0.3:7003
Ошибка означает, что мы со стороны приложения пытаеся приконнектится к Узлу редис кластера, используя IP докер контейнера и внутренний порт (порт 7003 используется данным узлом, но наружу он отображается на какой-то случайный порт, который мы и должны использовать в нашем приложении; внутренний порт, по понятным причинам, не доступен из вне). Что касается данного IP-адреса — он доступен для приложения, если это Linux, и он не доступен для приложения, если это MacOs/Windows (из-за особенностей реализации докера на этих ОС). Решение проблемы (а-ка костыль) я собрал по частичкам из разных статей. А давайте сделаем NAT RedisURI на стороне приложения. Ведь это нужно именно для тестов, и тут не так страшно вставлять такой ужас. Решение, на самом деле, состоит из пары строк (огромное спасибо Спрингу и Lettuce, где можно сконфигурировать практически всё, только и успевай, как переопределять бины). public SocketAddress resolve(RedisURI redisURI) {
Integer mappedPort = redisClusterNatPortMapping.get(redisURI.getPort()); if (mappedPort != null) { SocketAddress socketAddress = redisClusterSocketAddresses.get(mappedPort); if (socketAddress != null) { return socketAddress; } redisURI.setPort(mappedPort); } redisURI.setHost(DockerClientFactory.instance().dockerHostIpAddress()); SocketAddress socketAddress = super.resolve(redisURI); redisClusterSocketAddresses.putIfAbsent(redisURI.getPort(), socketAddress); return socketAddress; } Полный код выложен на гитхаб — https://github.com/Hixon10/spring-redis-cluster-testcontainers. Идея кода супер простая. Будем хранить две Map. В первой — маппинг между внутренними портами редиса (7000..7005) и теми, что доступны для приложения (они могут быть чем-то типа 51343, 51344 и тд). Во-второй — внешние порты (типа, 51343) и SocketAddress, полученный для них. Теперь, когда мы получаем от Редиса при обновлении топологии что-то типа 172.17.0.3:7003, мы сможем легко найти нужный внешний порт, по которому сможем найти SocketAddress и переиспользовать его. То есть, с портами проблема решена. А что с IP? С IP-адресом всё просто. Тут нам на помощь приходят Тест контейнеры в которых есть утилитный метод — DockerClientFactory.instance().dockerHostIpAddress(). Для MacOs/Windows он будет отдавать localhost, а для linux — IP-адрес контейнера. Выводы Программирование — это супер интересно, но это вы и без меня знали. А ещё, порой приходится вспомить, что было на первой лекции по сетям в универе, чтобы написать на своей любимой джавке пару новых интеграционных тестов. Приятно, когда знания из института пригождаются в самый неожиданный момент. =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_java, #_promyshlennoe_programmirovanie ( Промышленное программирование ), #_testirovanie_vebservisov ( Тестирование веб-сервисов ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:38
Часовой пояс: UTC + 5