Pagsasalin at pag-aangkop ng Java Microservices: A Practical Guide . Mga nakaraang bahagi ng gabay:
- mga pangunahing kaalaman sa microservice at ang kanilang arkitektura ;
- pag-deploy at pagsubok ng mga microservice .
Paano gawing nababanat ang microservice ng Java?
Alalahanin na kapag gumawa ka ng mga microservice, talagang ipinagpapalit mo ang mga tawag sa pamamaraan ng JVM para sa mga sabay-sabay na HTTP na tawag o asynchronous na pagmemensahe. Bagama't halos garantisadong makumpleto ang isang method call (maliban sa hindi inaasahang pag-shutdown ng JVM), hindi maaasahan ang isang tawag sa network bilang default. Maaari itong gumana, ngunit maaaring hindi ito gumana sa iba't ibang dahilan: ang network ay na-overload, isang bagong panuntunan sa firewall ay ipinatupad, at iba pa. Upang makita kung paano ito gumagawa ng pagkakaiba, tingnan natin ang halimbawa ng BillingService.Mga Pattern ng Resilience ng HTTP/REST
Sabihin nating ang mga customer ay maaaring bumili ng mga e-book sa website ng iyong kumpanya. Upang gawin ito, nagpatupad ka lang ng microservice sa pagsingil na maaaring tumawag sa iyong online na tindahan upang bumuo ng mga aktwal na PDF invoice. Sa ngayon, gagawin namin ang tawag na ito nang sabay-sabay, sa pamamagitan ng HTTP (bagama't mas makatuwirang tawagan ang serbisyong ito nang asynchronous, dahil hindi kailangang madalian ang pagbuo ng PDF mula sa pananaw ng user. Gagamitin namin ang parehong halimbawa sa susunod seksyon at tingnan ang mga pagkakaiba).@Service
class BillingService {
@Autowired
private HttpClient client;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
httpClient.send(invoiceRequest(user.getEmail(), invoice), responseHandler());
// ...
}
}
Upang buod, narito ang tatlong posibleng resulta ng HTTP na tawag na ito.
- OK: natuloy ang tawag, matagumpay na nalikha ang account.
- DELAY: Natapos ang tawag, ngunit napakatagal bago makumpleto.
- ERROR. Nabigo ang tawag, maaaring nagpadala ka ng hindi tugmang kahilingan, o maaaring hindi gumagana ang system.
Messaging Resilience Patterns (Отказоустойчивые паттерны обмена messagesми)
Давайте подробнее рассмотрим асинхронную коммуникацию. Наша программа BillingService теперь может выглядеть примерно так, при условии, что мы используем Spring и RabbitMQ для обмена messagesми. Whatбы создать счет, мы теперь отправляем сообщение нашему брокеру сообщений RabbitMQ, где есть несколько работников, ожидающих новых сообщений. Эти работники создают счета в формате PDF и отправляют их соответствующим пользователям.@Service
class BillingService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
// преобразует счет, например, в json и использует его How тело messages
rabbitTemplate.convertAndSend(exchange, routingkey, invoice);
// ...
}
}
Теперь потенциальные ошибки выглядят немного иначе, так How вы больше не получаете немедленных ответов OK or ERROR, How это было с синхронным HTTP-соединением. Вместо этого у нас может быть три потенциальных варианта неправильного развития событий, которые могут вызвать следующие вопросы:
- Было ли мое сообщение доставлено и использовано работником? Или это потеряно? (Пользователь не получает счет-фактуру).
- Мое сообщение было доставлено только один раз? Или доставлено более одного раза и обрабатывается только один раз? (Пользователь получит несколько счетов).
- Конфигурация: От «Использовал ли я правильные ключи маршрутизации/имена для обмена» до «Правильно ли настроен и поддерживается мой брокер сообщений or переполнены его очереди?» (Пользователь не получает счет-фактуру).
- Если вы используете реализации JMS, например, ActiveMQ, вы можете обменять speed на гарантии двухфазных (XA) коммитов (two-phase (XA) commits).
- Если вы используете RabbitMQ, для начала прочитайте это руководство, а затем хорошенько обдумайте подтверждения, отказоустойчивость и надежность сообщений в целом.
- Возможно, кто-то хорошо разбирается в конфигурировании serverов Active or RabbitMQ, особенно в сочетании с кластеризацией и Docker (кто-нибудь?;))
Какой фрейморк будет лучшим решением для микросервисов Java?
С одной стороны, можно установить очень популярный вариант, такой How Spring Boot. Он позволяет очень легко создавать файлы .jar, поставляется со встроенным веб-serverом, таким How Tomcat or Jetty, и который можно запустить быстро и где угодно. Идеально подходит для создания приложений микросервиса. Не так давно появилась пара специализированных микросервисных фреймворков Kubernetes or GraalVM, частично вдохновлённых реактивным программированием. Вот ещё несколько интересных претендентов: Quarkus, Micronaut, Vert.x, Helidon. В конце концов, вам придется выбирать самостоятельно, но мы можем дать вам парочку рекомендаций, возможно, не вполне стандартных: За исключением Spring Boot, все платформы микросервисов обычно позиционируются How невероятно быстрые, с почти мгновенным запуском, малым объемом используемой памяти, возможностью масштабирования до бесконечности. В маркетинговых материалах обычно фигурируют впечатляющие графики, представляющие платформу в выгодном свете рядом с “бегемотом” Spring Boot or друг с другом. Это по идее щадит нервы разработчиков, поддерживающих легаси-проекты, которые порой загружаются по несколько minutes. Или разработчикам, работающим в облаке, которые хотят запустить\остановить столько микроконтейнеров, сколько им сейчас нужно в течение 50 мс. Проблема, однако, заключается в том, что такое (искусственное) время старта «голого железа» и время повторного развертывания едва ли влияют на общий успех проекта. По крайней мере влияют гораздо меньше, чем сильная инфраструктура фреймворка, сильная documentация, сообщество и сильные навыки разработчика. Так что лучше смотреть на это так: Если до сих пор:- Вы позволяете своим ORM работать в режиме безудержной генерации сотен requestов для простых рабочих процессов.
- Вам нужны бесконечные гигаbyteы для запуска вашего монолита умеренной сложности.
- У вас так много codeа и сложность столь высока (сейчас мы говорим не о потенциально медленных стартерах, таких How Hibernate), что вашему приложению нужно несколько minutes для загрузки.
Какие библиотеки лучше всего подходят для синхронных вызовов Java REST?
На низкоуровневой технической стороне вы, вероятно, получите одну из следующих клиентских библиотек HTTP: Собственный HttpClient Java (начиная с Java 11), HttpClient Apache or OkHttp. Обратите внимание, что здесь я говорю «вероятно», потому что есть и другие варианты, начиная со старых добрых клиентов JAX-RS до современных клиентов WebSocket. В любом случае, существует тенденция к генерации HTTP-клиента, с отходом от самостоятельной возни с HTTP-вызовами. Для этого вам нужно взглянуть на проект OpenFeign и его documentацию в качестве отправной точки для дальнейшего чтения.Какие брокеры являются лучшими для асинхронного обмена messagesми Java?
Скорее всего, вы столкнётесь с популярными ActiveMQ (Classic or Artemis), RabbitMQ or Kafka.- ActiveMQ и RabbitMQ являются традиционными, полноценными брокерами сообщений. Они предполагают взаимодействие “умного брокера” и “глупеньких пользователей”.
- Исторически ActiveMQ имел преимущество простого встраивания (для тестирования), которое можно смягчить с помощью настроек RabbitMQ/Docker/TestContainer.
- Kafka нельзя назвать традиционным “умным” брокером. Наоборот, это относительно «глупое» хранorще сообщений (файл журнала), для обработки которого нужны умные потребители.
Какие библиотеки я могу использовать для тестирования микросервисов?
Это зависит от вашего стека. Если у вас развёрнута экосистема Spring, будет разумно использовать специальные инструменты этого фреймворка. Если JavaEE — что-то вроде Arquillian. Возможно, стоит взглянуть на Docker и действительно хорошую библиотеку Testcontainers, которая помогает, в частности, легко и быстро настроить базу данных Oracle для локальных тестов разработки or интеграции. Для мок-тестов целых HTTP-serverов, обратите внимание на Wiremock. Для тестирования асинхронного обмена messagesми попробуйте внедрить ActiveMQ or RabbitMQ, а затем написать тесты с помощью Awaitility DSL. Кроме этого, применяются все ваши привычные инструменты — Junit, TestNG для AssertJ и Mockito. Обратите внимание, что это далеко не полный список. Если вдруг вы не нашли здесь вашего любимого инструмента, опубликуйте его в разделе комментариев.Как включить логирование для всех микросервисов Java?
Логирование в случае с микросервисами — интересная и довольно сложная тема. Вместо того, чтобы иметь один файл лога, с которым вы можете работать посредством команд less or grep, теперь у вас есть n файлов логирования, и желательно, чтобы они не были слишком разрознены. Хорошо расписаны особенности экосистемы логирования в этой статье (на английском языке). Обязательно прочитайте его, и обратите внимание на раздел Централизованное ведение лога с точки зрения микросервисов. На практике вы столкнётесь с различными подходами: Системный администратор пишет определённые сценарии, которые собирают и объединяют файлы логов с различных serverов в один файл логов и помещают их на FTP-serverы для загрузки. Запуск комбинаций cat/grep/unig/sort в параллельных SSH-сессиях. Именно так поступает Amazon AWS, о чём вы можете сообщить своему менеджеру. Используйте такой инструмент, How Graylog or ELK Stack (Elasticsearch, Logstash, Kibana)Как мои микросервисы находят друг друга?
До сих пор мы предполагали, что наши микросервисы знают друг о друге, знают соответствующий IPS. Поговорим о статической настройке. Итак, наш банковский монолит [ip = 192.168.200.1] знает, что ему нужно поговорить с риск-serverом [ip = 192.168.200.2], который захардкожен в файле properties. Однако вы можете сделать всё более динамичным:- Используйте облачный server конфигурации, с которого все микросервисы извлекают свои конфигурации instead of развертывания файлов application.properties на своих микросервисах.
- Поскольку экземпляры ваших служб могут динамически менять свое местоположение, стоит присмотреться к службам, которые знают, где живут ваши службы, Howие у них IP и How их маршрутизировать.
- Теперь, когда все динамично, появляются новые проблемы, такие How автоматическое избрание лидера: кто является мастером, который работает над определенными задачами, чтобы например, не обработать их дважды? Кто заменяет лидера, когда он терпит неудачу? По Howому принципу происходит замена?
Как организовать авторизацию и аутентификацию с помощью микросервисов Java?
Эта тема также достойна отдельного рассказа. Снова таки, варианты варьируются от захардкоженной базовой аутентификации HTTPS с самописными фреймфорками безопасности до запуска установки Oauth2 с собственным serverом авторизации.Как убедиться, что все мои окружения выглядят одинаково?
То, что верно для развертываний без микросервиса, также верно и для развертываний с ним. Попробуете комбинацию Docker/Testcontainers, а также Scripting/Ansible.Не вопрос: кратко о YAML
Давайте ненадолго отойдём от библиотек и связанных с ними вопросов и вкратце рассмотрим Yaml. Этот формат file, используется де-факто в качестве формата для «записи конфигурации в виде codeа». Используют его и простые инструменты, наподобие Ansible и гиганты вроде Kubernetes. Whatбы испытать боль от отступов в YAML, попробуйте написать простой Ansible-файл и посмотрите, сколько вам придётся редактировать файл прежде, чем он заработает How нужно. И это невзирая на поддержку формата всеми крупными IDE! После этого возвращайтесь, чтобы дочитать это руководство.Yaml:
- is:
- so
- great
А How насчет распределенных транзакций? Тестирование производительности? Другие темы?
Может, когда-нибудь, в следующих редакциях руководства. А пока — всё. Оставайтесь с нами!Концептуальные проблемы микросервисов
Помимо специфических проблем микросервисов в Java, есть и другие проблемы, скажем, те, которые появляются в любом микросервисном проекте. Они касаются по большей мере организации, команды и управления.Несоответствие Frontend и Backend
Несоответствие Frontend и Backend — весьма распространенная проблема многих микросервисных проектов. What она означает? Лишь то, что в старых добрых монолитах, у разработчиков веб-интерфейса был один конкретный источник для получения данных. В микросервисных проектах у разработчиков веб-интерфейса неожиданно появляются n источников для получения данных. Представьте, что вы создаете Howой-то проект микросервисов IoT (интернет вещей) на Java. Скажем, заведуете геодезическими машинами, промышленными печами по всей Европе. И эти печи регулярно отправляют вам обновления с указанием их температуры и тому подобными данными. Рано or поздно вы, возможно, захотите найти печи в пользовательском интерфейсе администратора, возможно, с помощью микросервисов «поиска печи». В зависимости от того, насколько строго ваши бэкэнд-коллеги применяют предметно-ориентированное проектирование or законы микросервисов, микросервис “найти печь” может возвращать только идентификаторы печей, а не другие данные, такие How тип, модель or местоположение. Для этого фронтенд-разработчикам нужно будет выполнить один or n дополнительных вызовов (в зависимости от реализации пейджинга) в микросервисе «получить данные о печи» с идентификаторами, которые они получor от первого микросервиса. И хотя это всего лишь простой пример, пускай и взятый из реального (!) проекта, даже он демонстрирует следующую проблему: супермаркеты стали чрезвычайно популярны. А всё потому, что с ними вам не нужно идти в 10 разных мест, чтобы купить овощи, лимонад, замороженную пиццу и туалетную бумагу. Вместо этого вы идете в одно место.Это проще и быстрее. То же самое касается разработчиков интерфейсов и микросервисов.Ожидания руководства
У менеджмента складывается ошибочное впечатление, что теперь нужно нанимать бесконечное количество разработчиков во (всеобъемлющий) проект, поскольку разработчики теперь могут работать совершенно независимо друг от друга, каждый на своем микросервисе. В самом конце требуется лишь небольшая работа по интеграции (незадолго до запуска). На самом деле такой подход крайне проблематичен. В следующих параграфах мы постараемся пояснить, почему.“Меньшие кусочки” не равно “лучшие кусочки”
Будет большой ошибкой считать, что разделённый на 20 частей code обязательно будет качественнее одного цельного куска. Даже если взять качество с сугубо технической точки зрения: наши отдельные службы по-прежнему могут выполнять 400 requestов Hibernate для выбора пользователя из базы данных, проходясь по слоям не поддерживаемого codeа. В очередной раз возвращаемся к цитате Саймона Брауна: если не удастся построить монолиты должным образом, будет сложно создать надлежащие микросервисы. Зачастую об отказоустойчивости в микросервисных проектах крайне несвоевременно. Настолько, что порой страшно смотреть, How микросервисы работают в настоящих проектах. Причина этого кроется в том, что Java-разработчики не всегда готовы изучать отказоустойчивость, сети и другие смежные темы на должном уровне. Сами “кусочки” — меньше, а вот “технических частей” — больше. Представьте, что вашей микросервисной команде предлагается написать технический микросервис для входа в систему базы данных, примерно такой:@Controller
class LoginController {
// ...
@PostMapping("/login")
public boolean login(String username, String password) {
User user = userDao.findByUserName(username);
if (user == null) {
// обработка варианта с несуществующим пользователем
return false;
}
if (!user.getPassword().equals(hashed(password))) {
// обработка неверного пароля
return false;
}
// 'Ю-ху, залогинorсь!';
// установите cookies, делайте, что угодно
return true;
}
}
Теперь ваша команда может решить (и, возможно, даже убедить людей бизнеса), дескать, это всё слишком просто и скучно, лучше instead of службы входа в систему написать действительно полезный микросервис UserStateChanged (изменение состояния пользователя) без Howих-либо реальных и ощутимых бизнес-требований. А поскольку к Java в настоящее время некоторые люди относятся How к динозавру, напишем наш микросервис UserStateChanged на модном Erlang. И давайте попробуем где-нибудь использовать красно-черные деревья, потому что Стив Йегге написал, что вы должны знать их изнутри, чтобы подать заявку в Google. С точки зрения интеграции, обслуживания и общего проекта это так же плохо, How написание слоев спагетти-codeа внутри одного монолита. Искусственный и заурядный пример? Так и есть. Тем не менее, подобное может быть и в реальности.
Меньше кусочки — меньше понимания
Затем естественным образом всплывает вопрос о понимании системы в целом, её процессов и рабочих потоков, но при этом вы, How разработчик, несете ответственность только за работу на своём изолированном микросервисе [95: login-101: updateUserProfile]. Он гармонирует с предыдущим параграфом, но в зависимости от вашей организации, уровня доверия и коммуникации это может привести к большому количеству недоумения, пожиманий плечами, обвинениям в случае случайной поломки в микросервисной цепочке. И нет того, кто бы принял на себя полную ответственность за случившееся. И дело вовсе не в недобросовестности. На самом деле очень трудно соединить разные детальки и понять их место в общей картине проекта.Коммуникации и обслуживание
Уровень коммуникации и обслуживания сильно зависит от размера компании. Тем не менее, общая зависимость очевидна: чем больше, тем проблематичнее.- Кто работает на микросервисе № 47?
- Они только что развернули новую несовместимую версию микросервиса? Где это было заdocumentировано?
- С кем мне нужно поговорить чтобы requestить новую функции?
- Кто будет поддерживать тот микросервис на Erlang, после того, How единственный кто знал этот язык покинул компанию?
- Все наши микросервисные команды работают не только на разных языках программирования, но и в разных часовых поясах! Как мы всё это правильно скоординируем?
GO TO FULL VERSION