В 26-й выпуск В подкасте, посвященном NP, я рассказал вам, что начал мигрировать один из своих сервисов с Redis Sentinel на Redis Cluster. На этой неделе мне захотелось протестировать этот код, и я, конечно же, выбрал для этого Testcontainers. К сожалению, Redis Cluster в тестовых контейнерах не запустился из коробки, и мне пришлось вставить несколько костылей.
О них пойдет речь дальше.
Вводный
Сначала хотелось бы описать всю вводную информацию, а потом уже говорить о костылях.Мой проект построен на Spring Boot. Для взаимодействия с редисом используется Салатный клиент .
Для тестирования - testcontainers-Java с JUnit. Версия обеих редиск - 6. В целом все типично, ничего особенного в плане стека нет. Если кто-то еще не знаком с тестконтейнерами, то пару слов о них.
Это библиотека для интеграционного тестирования.
Он построен на другой библиотеке - https://github.com/docker-java/docker-java .
Тестовые контейнеры, по сути, помогают вам быстро и легко запускать контейнеры с различными зависимостями в ваших интеграционных тестах.
Обычно это базы данных, очереди и другие сложные системы.
Некоторые люди также используют тест-контейнеры для запуска своих сервисов, от которых зависит тестируемое приложение (для тестирования взаимодействия микросервисов).
О кластере Redis
Redis Cluster — одна из нескольких реализаций распределенного режима Redis. К сожалению, в Redis нет единственно правильного способа масштабирования базы данных.Есть Sentinel, есть Redis Cluster, а еще ребята активно развивают RedisRaft — распределённую редиску на основе протокола консенсуса Raft (у них есть своя реализация, которая, как они сами говорят, не совсем каноничная Raft, но специально для Редис, это в самый раз).
А вообще про Redis Cluster на официальном сайте есть две отличные статьи — https://redis.io/topics/cluster-tutorial И https://redis.io/topics/cluster-spec .
Там описана большая часть деталей.
Чтобы использовать Redis Cluster в тестовых контейнерах, важно знать кое-что из документации.
Во-первых, Redis Cluster использует протокол Gossip, поэтому каждый узел кластера имеет TCP-соединение со всеми остальными узлами.
Следовательно, между узлами должна быть сетевая связь, даже в тестах.
Вторая важная вещь, которую нужно знать при тестировании, — это наличие узлов конфигурации в загрузочной загрузке Redis Cluster. То есть в настройках вы можете указать только подмножество узлов, которые будут использоваться для запуска приложения.
Впоследствии клиент Redis сам получит топологию кластера посредством взаимодействия с Redis. Исходя из этого, получается вторая особенность — тестируемое приложение должно иметь сетевое подключение с теми Redis URI, которые будут анонсированы из Redis-кластера (кстати, эти адреса можно настроить через кластер-анонс-порт И кластер-анонс-IP ).
О костылях с Redis Cluster и тестконтейнерах
Для тестирования я выбрал достаточно популярный образ докера — https://github.com/Grokzen/docker-redis-cluster .Для производства он не пригоден, но его очень легко использовать в тестах.
Особенность этого образа в том, что все Редиски (а их 6, по умолчанию — 3 мастера и 3 слейва) будут подняты в пределах одного контейнера.
Таким образом, мы автоматически получаем сетевое соединение между узлами кластера из коробки.
Осталось решить вторую из двух задач, связанных с получением приложением топологии кластера.
Я не хотел создавать свой образ Docker, а выбранный мной образ не предоставляет возможности установки настроек.
кластер-анонс-порт И кластер-анонс-IP .
Поэтому, если вы ничего дополнительно не сделаете, то при запуске тестов вы увидите такие ошибки:
Ошибка означает, что мы на стороне приложения пытаемся подключиться к узлу кластера Redis, используя IP-адрес докер-контейнера и внутренний порт (порт 7003 используется этим узлом, но внешне он сопоставлен с каким-то случайным портом, который мы в нашем приложении следует использовать внутренний порт, по понятным причинам недоступный извне).Unable to connect to [172.17.0.3/<unresolved>:7003]: connection timed out: /172.17.0.3:7003
Что касается этого IP-адреса, то он доступен приложению, если это Linux, и недоступен приложению, если это MacOs/Windows (из-за особенностей реализации докера на этих ОС).
Решение проблемы (он же костыль) собирал по кусочкам из разных статей.
Давайте сделаем NAT RedisURI на стороне приложения.
Ведь это нужно именно для тестов, и не так страшно сюда такую жуть вставлять.
Решение, по сути, состоит из пары строчек (большое спасибо Spring и 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;
}
Полный код опубликован на GitHub. .
Идея кода очень проста.
Мы будем хранить два карта .
Первый включает в себя сопоставление внутренних портов редиса ( 7000.7005 ) и те, которые доступны приложению (они могут быть чем-то вроде 51343, 51344 и т. д).
Во-вторых, внешние порты (например, 51343 ) И Адрес сокета получено за них.
Теперь когда мы получаем от Redis при обновлении топологии что-то вроде 172.17.0.3:7003 , мы легко найдем нужный внешний порт, по которому можно найти Адрес сокета и использовать его повторно.
То есть проблема с портами решена.
А что насчет ИП? С IP-адресом все просто.
Здесь нам на помощь приходят тестовые контейнеры, которые содержат служебный метод — DockerClientFactory.instance().
dockerHostIpAddress() .
Для MacOs/Windows это даст локальный хост , а для Linux — IP-адрес контейнера.
выводы
Программирование — это очень интересно, но вы и без меня это знали.Также иногда приходится вспомнить, что было на первой лекции по сетям в университете, чтобы написать пару новых интеграционных тестов на любимой Java. Приятно, когда знания института пригодятся в самый неожиданный момент. Теги: #программирование #redis #Тестирование веб-сервисов #java #Промышленное программирование #тестовые контейнеры #spring
-
Штаудингер, Герман
19 Oct, 24 -
Проект Тукан
19 Oct, 24 -
12 Советов По Масштабированию Node.js
19 Oct, 24 -
Взломанная Drm-Защита Allofmp3
19 Oct, 24