Comments 32
Очень приятное описание и чистенький пример, класс. Хочется чтобы примеры и статьи такого уровня находились в процессе поиска почаще, ну и не устаревали :)
Статья есть на английском? Хочется видеть её тут — https://spring.io/blog
Статья на английском сейчас в процессе модерации на DZone.com, но я обязательно изучу возможность публикации в блоге Cпринга (мне казалось там пишет только сама команда).

Кирилл, спасибо за отзыв! Очень жду ваших докладов на JPoint.
Рад слышать. Отличный шанс развиртуализироваться :)

На счет пишут ли там не свои — вроде как тоже заметил, что там только «свои» статьи. Но ведь всегда можно быть первым.
Я бы пошел сюда https://gitter.im/spring-cloud/spring-cloud и задал вопрос Dave Syer (@dsyer) о том как это сделать или обратился в твиттере к @starbuxman. Они нормально идут на контакт)
Спасибо за совет! Дэйв сказал, что как раз для таких случаев Джош Лонг каждую неделю собирает сторонние публикации в посте This Week in Spring, видимо пока это единственный вариант. Сегодня появился свежий пост с упоминанием о статье.
Всегда пожалуйста. Немного не то чего хотелось конечно, но и за это спасибо Джошу :)
Шикарная статья! Расскажите, а вы тестировались время отклика при падении лидера? Быстро ли Приложение приходит в консистентное состояние?
Спасибо за отзыв. Пока не проводил сколько-нибудь серьезных тестов на этот счет, будет интересно заняться этим отдельно. Если я правильно понял ваш вопрос: при падении Докер рестартует инстанс достаточно быстро, загрузка Spring Boot приложения занимает менее 30 сек, сразу происходит запрос в Service Discovery. На дефолтных настройках Eureka может потребоваться до полутора минут, чтобы клиент увидел инстанс. Эти настройки можно изменить, но именно их Netflix рекомендует для продакшена.
Вопрос о микросервисах в общем: если для какой-то страницы UI необходимо дернуть ну хотя бы 5 сервисов, и если время ответа каждого сервиса ну хотя бы 50-60мс — это уже 300мс на 1 запрос, не считая времени на основной, первоначальный, запрос.

Что я в этой схеме не понимаю? Как можно делать быстрые приложения на микросервисах с синхронными внутренними запросами?
Никто не запрещает вам делать асинхронные запросы. Насколько я понял тут синхронные запросы приводятся для упрощения описания.
Коммуникация между сервисами также существенно упрощена: используются только синхронные rest-запросы.
Как можно делать быстрые приложения на микросервисах с синхронными внутренними запросами?

Мне не известны способы сделать цепочку синхронных запросов быстрой, ведь оверхэд на сеть будет всегда.

Другой вопрос, что все стремятся сделать взаимодействие максимально асинхронным. В случае, если для страницы UI вам необходимо дернуть 5 сервисов для сбора контента — есть возможность распараллелить такой запрос сразу в API Gateway, это одна из его основных задач. Существует стремительно развивающаяся дисциплина — реактивное программирование (посмотрите Akka, RxJava). Есть немало материала на этот счет, в том числе на хабре. Короткая статья Netflix как раз на эту тему.

С другой стороны, стоит помнить, что существует два разных подхода к взаимодействию — оркестрация и хореография:


Последний позволяет решить много проблем в распределенной системе, делая узлы менее связанными, со всеми вытекающими плюсами.
Кроме того, одним из самых сложных моментов является разбиение монолита. И если требуется цепочка из пяти непременно синхронных запросов, возможно стоит пересмотреть гранулярность такой микросервисной системы.
Если речь про Latency, то в случае необходимости синхронных вызовов всегда есть некоторый trade-off. Но никто вам не мешает сделать специализированный микросервис, который будет обеспечивать маленькие задержки и содержать в себе несколько больше логики, жертвуя при этом какими либо принципами. Всегда можно поиграться параметрами.
Тут важно то, что это Latency можно сохранять с увеличивающейся нагрузкой. Т.е если к пришло 100rps и задержки были 300мс. То распределенную систему можно быстро отмасштабировать на работу с 10000rps при тех же 300мс. Мне кажется, упор именно на это.
Спасибо за качественную статью, а картинки просто красота :)
Меня немного пугает сложность администрирования все этого «хозяйства». Представим что мы не имеем права на single point of failure, тогда нам нужно как минимум по 2 инстанса каждого сервиса, в итоге даже на таком примере у нас уже десяток серверов приложений и баз данных.
Также рекомендую статью все того же Мартина Microservice Trade-Offs
Механизмы оркестрации контейнеров, Continuous Delivery и Service Discovery призваны автоматизировать вопросы администрирования (что конечно никак не уменьшает сложности на этапе возведения).

А single point of failure — о другом, это грех классического монолита или расшаренной между сервисами базой данных. Выдуманный пример: в некоторый момент на вход начинают поступать данные, вызывающие критическую ошибку в Statistics Service, приводящую к падению всего приложения. В случае монолита это последовательно уронит все инстансы, пользователи не смогут доступится к приложению вообще. В случае микросервисов нет единой точки отказа — Account Service и Notification Service продолжают фунционировать (все загружается и сохраняется, разве что на UI не работает график про статистику), а после исправления дефекта поднятые инстансы Statistics Service разберут накопившиеся очереди.
И правда отличная статья!
Сами сейчас копаем в сторону Yandex.Cocaine, поэтому было особо интересно посмотреть, как в другом мире обстоят дела.
А как вы проводите интеграционное тестирование всех этих сервисов? Я имею ввиду — как вы его автоматизируете?
А в чем тут отличие от тестирования монолита?

Есть эталонный проект от spring-cloud — brewery sample. В нем есть тесты, можно посмотреть. Если речь про интеграционные или приемочные тесты, то тестируется же примерно так же как и монолит. Но при этом вы имеете дополнительные возможности в тестировании отдельных частей.
Спасибо за статью. А можно еще ссылку на сам сервис личных финансов? Красиво на картинке выглядит, хотелось бы пощупать :)
Расчет был на то, что красивая картинка сподвигнет интересующихся разобраться и самостоятельно стартовать систему, по дороге узнавая что-нибудь новое. Но если хочется просто потыкать UI времен раннего студенческого творчества, на rhcloud доступна старая версия.
Хорошая архитектура, мы пришли примерно к такой же. Из Вашей статьи не понятно правда как делается автоматическое масштабирование приложения при увеличении нагрузки или тут нет такого функционала?
У нас пока не стояло задачи скейлить инстансы автоматически, и это точно выходит за рамки данной статьи. Но оркестрация большого числа контейнеров и автомасштабирование — большая и крайне интересная тема. Есть инструмент от самого докера — Docker Swarm, есть Google Kubernetes. Облачные провайдеры предоставляют специальные сервисы.
Спасибо за статью, Александр.

Хочтелось бы продолжения данной темы, с ответами на следующие вопросы:
1. Деплой приложения «одной кнопкой» без даунтайма (после обновления одного\нескольких компонентов).
2. Бекапы и развертывание приложения из бекапов после падения серевра(ов) (ситуация, типа «взрыв в датацентре»)
3. Наращивание количества серверов в случае роста нагрузки и распределение запросов между новыми серверами. Желательно, чтобы процедура добавления\удаления сервера из кластера реализовывалась также «одной кнопкой».
Очень качественная статья, спасибо. Про Spring Cloud + Spring Boot читал раньше, то, как они автоматизируют какие-то типовые вещи — конечно, мега. Обнаружение серверов и поддержание конфигураций, настройка все на уровне java-аннотаций — мега мощно. Ну и микросервисные фреймворки + библиотеки от Нетфликса интегрировались очень органично.
Если я бы сейчас выбирал стек фреймворков и библиотек для самостоятельной реализации облака микросервисов — в первую очередь смотрел бы в сторону спринга.
Отличная статья, спасибо. Только есть вопрос по контейниризации и автоматизации деплоя. На одном хосте может быть задеплоено несколько инстансов одного логического сервиса. Eureka «знает» о хостах и портах самих приложений, но они совсем не обязательно будут матчится с портами, открытыми наружу через docker. Как вы решаете эту проблему? Или исходите из того, что у каждого нового запущенного контейнера открытый порт будет совпадать с портом приложения? Из примера это не очевидно, т.к. порты захардкожены в config проекте.

Спасибо за отзыв, но кажется вы не понимаете ряд важных моментов.


  1. В .yml файлах из конфиг-сервиса ничего не захардкожено — там указаны порты, на которых должны подниматься спринговые приложения (это никак не связано с портами, которые торчат наружу). Порты здесь указаны они для удобства, ибо по умолчанию порт один — 8080 и если вы решите запустить их на одном хосте (из IDE, например) — будет конфликт. Но при запуске в контейнерах это вообще не важно — можете поменять их на стандартные, если хочется. Каждый контейнер со спринговым приложением будет экспоузить 8080, каждый со своего хоста внутри докер-нетворка.


  2. В продакшн моде наружу из докер нетворка выставляется только часть служебных сервисов (типа rabbitmq managment, eureka dashboard, мониторинг и конечно gateway на 80 порту), чем меньше тем лучше. Смотрите докер-компоуз файлы.


  3. Когда поднимается какой-нибудь account-service, он понятия не имеет где находится notification-serice — это нигде не прописано и ему предстоит узнать адрес у Eureka. Вы можете провести простой эксперимент — после запуска всей системы сделайте docker-compose scale notification-service=2. Поднимется еще один инстанс notification-service, зарегистрируется в Eureka и клиентские балансировщики у всех остальных сервисов будут раунд робином стучать в два имеющихся notification-service. Просто смотрите логи docker-compose logs notification-service и стучите в него через gateway: curl -X GET -H "Authorization: Bearer XXXX" http://127.0.0.1/notifications/recipients/current


  4. Часть служебных сервисов являются фиксированными точками. Такие точки каждый из сервисов знает наизусть (без Service Discovery):

  • Очевидно сам сервер Eureka — http://registry:8761
  • Конфиг сервер — http://config:8888. Этот адрес прописан прямо в bootstrap.yml каждого из микросервисов, ибо это первое, куда им нужно обратиться при старте — "Config First" mode. См. документацию.
  • Авторизационный сервер — http://auth-service:5000. Вот его хотелось бы регистрировать в Service Discovery, но пока это невозможно из коробки. См. тикет на гитхабе.
    Как видно, ко всем ним можно обращаться по алиасам внутри докер нетворка, что делается с помощью встроенного dns-сервера (компоуз запускает контейнеры с командой --net-alias, в результате имя в сети = имени сервиса) — см. документацию
Да, безусловно, возможно я слишком кратко и сумбурно изложил вопрос. Понятно, что есть N-ный набор сервисов, локация которых условно неизменна. Речь шла о том, что порты приложений никакой роли не играют, важно то, на какие порты хоста они смаплены в рамках контейнеров. И да, обычно каждое приложение действительно открыто по порту 8080, а уже докер в момент поднятия контейнера мапит этот порт на следующий свободный порт хоста.

В случае, скажем, со стеком Consul/Consul template/Gliderlabs registrator/Nginx — service registry консула сформирован на основе информации об открытых портах докер контейнеров, таким образом service discovery заточен, если можно так выразиться: на инфраструктуру, а не на код.

Если я правильно понимаю суть Eureka — в реестре сервисов хранится информация о хостах и портах spring boot (в нашем случае) приложений. Эти порты, смапленные на порты хостов (хостов может быть множество, также как и каждый логический сервис может иметь несколько инстансов в рамках одного хоста), не всегда будут равны портам самих приложений.

Та же связка consul/registrator здесь хороша тем, что service registry хранит информацию о портах уже контейнеров, а не приложений.

Как эта проблема решается Netflix OSS стеком?

Теперь понял о чем вы. Проблема встает если выставлять Service Discovery наружу из докер нетворка и регистрировать сервисы извне (не делал так). Решается очевидно тем же способом, кстати в gliderlabs/registrator, о котором вы говорите, я вижу свежий пулл-реквест для поддержки Eureka. Кроме того, во всем этом стеке Eureka можно заменить на Consul достаточно безболезненно. Ну и если докер соберется сделать вот этот тикет, проблема будет решена окончательно.

А каким образом отсылаются логи в ELK при такой архитектуре?
Beats процессы запускаются в каждом контейнере сервиса?
Или как-то можно перенаправить логи средствами докера?
можно менять у docker контейнеров log-driver. Мы используем syslog. А за syslog уже стоит софт который персистит лог ивенты в kafka (rsyslogd/syslog_ng). Оттуда уже разлетаются в том числе и в ELK.
Only those users with full accounts are able to leave comments. Log in, please.