Как Подключить Redis Cluster К Тестконтейнерам?

В 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 .

Поэтому, если вы ничего дополнительно не сделаете, то при запуске тестов вы увидите такие ошибки:

  
   

Unable to connect to [172.17.0.3/<unresolved>:7003]: connection timed out: /172.17.0.3:7003

Ошибка означает, что мы на стороне приложения пытаемся подключиться к узлу кластера Redis, используя IP-адрес докер-контейнера и внутренний порт (порт 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

Вместе с данным постом часто просматривают: