Мікросервіси в Java, або Як пройшов мій перший воркшоп у ролі спікера
Привіт, світе! Мене звати Єгор, і я радий представити свою першу статтю на DOU. В мене більше
DataArt Summer IT Camp — це двотижневий онлайн-інтенсив, який відбувся у липні. Учасники дізнались про новітні технології в ІТ, розвивали soft skills та поглиблювали знання під час практичних воркшопів. Усі ресурси були доступні для перегляду в асинхронному форматі, що дозволяло зручно підлаштуватися під різницю в часових поясах. Однак для практичних воркшопів учасникам потрібно було долучитися онлайн, а в Тбілісі та Львові навіть була можливість відвідати офіси DataArt для офлайн-семінарів.
Цей Camp зібрав 1662 учасники з 50 країн, і я пишаюсь тим, що став частиною грандіозного заходу. Я погодився провести воркшоп з Java, проте через інтенсивний графік роботи на проєкті, ми зібрали команду з
А тепер більш детально про сам воркшоп — план був такий:
- Вступний квіз на знання Java, для того, щоб створити всім робочий настрій.
- Презентація про еволюцію архітектури програмного забезпечення.
- Реалізація мікросервісів на Java.
- Фінальний тест із засвоєного матеріалу.
Я не буду надто детально зупинятися на кожному з пунктів, оскільки воркшоп тривав 4 години і не хотілось би, щоб ви витратили стільки ж часу на прочитання цієї статті, тому я згадаю тільки головне.
Вступний квіз мав розважити слухачів та налаштувати на продуктивний вечір. Тут були питання на кшталт «Хто розробив Java або який клас є батьківським для всіх обʼєктів в Java?». Також були глибші питання, наприклад: що таке Optionals, Records, як працює GC тощо. Результати цього тесту допомогли мені зрозуміти, наскільки ґрунтовно авдиторія розуміє мову програмування, і скажу завчасно: мені пощастило з учасниками, оскільки більшість із них добре розбиралася в темі.
Щодо презентації, то тут я вирішив зосередитися на еволюції архітектури в програмуванні та пояснити різницю між основними архітектурними підходами від моноліту до наносервісів.
Моноліт
Розпочнемо з Моноліту (Monolith) — це найбільш древній та простий архітектурний підхід, оскільки тут особливо нічого вигадувати не потрібно: уся логіка в одному місці, усі компоненти, разом із БД та UI, є взаємоповʼязаними та взаємозалежними, а процеси розробки, тестування та розгортання відбуваються в одній уніфікованій системі. Загалом зобразити моноліт досить легко:
Раніше, ще до того як я потрапив в ІТ, не існувало іншої архітектури, окрім як моноліту, тому весь код був єдиним цілим і програмісти зіштовхувалися з великою кількістю проблем:
- складність до масштабування;
- повільні цикли розгортання;
- єдина точка відмови, тобто якщо помилка виникне в одному місці, то перестане працювати вся програма;
- менша гнучкість до змін та оновлень. Я памʼятаю, як під час роботи з монолітами не раз траплялися помилки через те, що зміни в одному класі впливали на поведінку в зовсім інших частинах програми. Зазвичай такі проєкти переростають в один величезний заплутаний шматок коду, з яким відмовляється працювати будь-хто.
З плюсів я тут можу виділити хіба:
- простоту розробки та розгортання;
- легкість у тестуванні.
Отож, де використання моноліту є доцільним? Попри те, що цей підхід є застарілим, все-таки трапляються випадки, коли краще використати його, адже:
- Підходить для невеликих додатків, де всі функції тісно пов’язані між собою.
- Ідеально підходить для швидкого створення прототипу додатку.
- Ефективний у додатках, де потрібна висока продуктивність, оскільки внутрішні виклики відбуваються швидше, ніж міжсервісні.
Сервіс-орієнтована архітектура (SOA)
Тепер поговоримо про наступне архітектурне рішення — сервіс-орієнтовану архітектуру (SOA). Майже всю свою карʼєру програміста я провів на проєктах саме з такою архітектурою. Це є проміжним етапом між монолітом та мікросервісами. Тут вся логіка вже не в одній програмі, а розбита на декілька сервісів, проте це не гарантує того, що один із сервісів не може виглядати як справжнісінький моноліт :)
Схематично цей архітектурний підхід ми можемо зобразити так:
Як ми можемо побачити зі схеми, бізнес-логіка в цьому підході розбита на декілька сервісів, кожен з яких використовується для конкретних бізнес-задач, але ми все ще маємо спільну БД. Також ми бачимо новий компонент ESB. Enterprise Service Bus — це програмне забезпечення для інтеграції застосунків у системі. ESB забезпечує спільне середовище, яке дозволяє різним застосункам взаємодіяти між собою, спрощуючи обмін повідомленнями та координацію процесів.
Такий підхід має певні переваги над монолітом:
- ми можемо перевикористовувати наші сервіси;
- легше модифікувати;
- уже немає єдиної точки відмови, якщо помилка станеться в якомусь із сервісів, то це не вплине на роботу інших;
Проте тут є певні недоліки:
- складно керувати міжсервісними звʼязками;
- можуть виникнути проблеми з продуктивністю через затримку в мережі;
- система залишається жорстко звʼязаною через наявність спільної БД — будь-які зміни в БД можуть вплинути на роботу інших програм.
SOA — це перевірений часом архітектурний підхід, який ідеально підійде:
- Для великих та складних систем, які вимагають високої гнучкості та адаптивності.
- Для організацій із широким спектром розрізнених систем, які потребують обміну даними.
- Коли певні бізнес-функції потрібні в декількох додатках.
Мікросервіси
Ось ми дійшли до найбільш популярного архітектурного рішення сьогодення — мікросервісів. Цей підхід зараз використовують усюди, де треба і не треба. Архітектура мікросервісів розробляє додаток як набір невеликих, слабко пов’язаних між собою сервісів. Кожен мікросервіс є незалежною сутністю з певною роллю і може розроблятися, розгортатися і масштабуватися незалежно. Мікросервіси взаємодіють один з одним через API, часто використовуючи HTTP / REST або асинхронний обмін повідомленнями. Головною відмінністю між мікросервісною архітектурою та SOA є наявність окремих баз даних для кожного сервісу. Це можна проілюструвати так:
На схемі цього не зображено, проте, як і в SOA, перед мікросервісами може бути ще один компонент, який часто називають Backend for Frontend (BFF). По суті, цей паттерн робить обгортку над API для кожного мікросервісу та створює єдину уніфіковану API для UI.
Серед переваг можна виокремити:
- висока масштабованість та гнучкість;
- стійкість;
- можливість використання різних технологій для кожного мікросервісу.
З мінусів:
- складний у керуванні;
- проблеми при узгодженні даних;
- проблеми з продуктивністю через затримки в мережі.
Цей підхід є надзвичайно ефективним для організацій, які прагнуть максимізувати продуктивність, дозволяючи командам працювати незалежно над різними сервісами. І звісно, ця архітектура ідеально підходить для великих і складних систем, які потребують високої масштабованості та гнучкості, тобто якщо потрібно масштабувати різні компоненти незалежно.
Наносервіси
Насамкінець, ми підійшли до найсвіжішого рішення — наносервісів. Дивлячись на схему, багато в кого може виникнути питання: а чим вони відрізняються від мікросервісів?
Наносервіси є ще меншими за мікросервіси. Кожен наносервіс виконує дуже обмежену функцію. Цей підхід може забезпечити ще більшу гнучкість та масштабованість, але також може ускладнити управління та розробку через велику кількість дрібних компонентів.
Здебільшого наносервіси асоціюються з безсерверною (serverless) архітектурою — це підхід до розробки та розгортання додатків, де розробники не потребують управляти серверами. Замість цього вони лише розробляють та розгортають код. Важливо розуміти, що serverless не означає відсутність серверів. Сервери справді існують, але їхнє управління, обслуговування та масштабування відбувається постачальником хмарних послуг за кулісами.
Коли говорять про наносервіси, то мають на увазі «функції як сервіс» (Function as a Service, FaaS), такі як AWS Lambda, Google Cloud Functions або Azure Functions. Оскільки я маю досвід тільки з AWS Lambda, тому на схемі я вказував їх та API Gateway, який виконує функції роутинга.
З переваг можна зазначити:
- автоматична масштабованість;
- відсутність управління серверами;
- сплачуєте тільки тоді, коли користуєтесь.
Мінуси наступні:
- підвищена складність в управлінні, моніторингу та налагодженні;
- затримка під час холодного старту, тобто під час першого виклику функції, вона виконуватиметься довше, оскільки потрібно підняти environment;
- потенційна привʼязка до постачальника, тобто якщо ми обрали AWS, то переїхати на Azure або GCP буде важко.
Цей архітектурний підхід можна застосувати для додатків з непередбачуваним попитом, оскільки такий додаток буде масштабуватись автоматично залежно від потреб. Також якщо ви не любите керувати інфраструктурою, наносервіси — чудовий вибір.
Практичне завдання
Для практичного завдання ми вирішили реалізувати мікросервісну архітектуру на прикладі старої-доброї онлайн-книгарні. Ми створили три мікросервіси на Java + SpringBoot + H2 (embedded DB) і ще один discovery-сервіс, який використовував Spring Eureka. Увесь код можна знайти за посиланням. А тут я розповім коротко про кожен мікросервіс, а також про те, що б ще можна було покращити в нашому завданні:
- server-eureka-java — інфраструктурний сервіс, який дозволяє виявляти інші мікросервіси нашої книгарні.
- service-authority-java — мікросервіс, за допомогою якого відбувається реєстрація та авторизація юзерів.
- service-bookmanager-java — тут відбуваються CRUD-операції над книжками, також цей сервіс може виконати виклик до service-ordercontroller-java, щоб отримати всі замовлення для конкретної книги.
- service-ordercontroller-java — тут створюються замовлення і робляться запити до service-bookmanager-java для отримання інформації про книжки.
Для того, щоб не витрачати час на live coding, ми вирішили кожен мікросервіс розбити на окремі бранчі, де з кожною новою бранчою «допилювали» функціонал. Я пояснював код, а учасники намагались писати за мною, а хто не встигав, той просто змінював гілку на наступну і мав уже готовий робочий код. Гілки для кожного сервісу були схожими та містили подібний функціонал:
- Ініціалізація проєкту.
- Необхідні DTO.
- Створення репозиторіїв.
- Створення сервісів.
- Створення контролерів.
Перевірку коректності роботи мікросервісів ми здійснювали за допомогою викликів у Postman. Для кожного мікросервісу ми підготували колекцію Postman запитів, щоб учасники не витрачали час на їхнє створення. Ви можете їх знайти в папці docs під назвою «назва_сервісу.postman_collection.json».
Висновки
І хоч я та учасники воркшопу залишилися задоволеним практичною частиною, а більшості вдалось підняти мікросервіси та побавитись із запитами, проте ідеалу не існує, і наш код — не виняток. Десь посеред воркшопу я зрозумів, що наш service-authority-java вийшов «відірваним» від інших мікросервісів, оскільки ми зовсім забули про те, що JWT токен, який генерується там, мав би прокидатись і в інші мікросервіси та перевірятися на валідність. А так склалося, що хоч у нас і є сервіс, який здійснює авторизацію, проте вона працює тільки в середині цього сервісу, а інші два сервіси могли приймати команди від будь-кого, що є неправильним. В ідеалі, кожен мікросервіс має мати секʼюріті та робити запити до service-authority-java для перевірки, чи валідний токен. У кінці виступу мені навіть поставили питання щодо цього: я відповів, що це сплановане рішення, щоб не заплутувати учасників-початківців ще більше. Звісно, що я лукавив.
Після практичної частини учасників чекав ще один фінальний тест — на цей раз складніший, що стосувався теми воркшопу та ще деяких вебінарів, які проходили раніше. Усі учасники, які успішно склали цей тест (набрали більше 65 %), отримали від нас сертифікати про успішне закінчення навчання.
Наша компанія вже розпочала підготовку до наступного DataArt Winter IT Camp. Поки я ще не отримував запрошення на участь, проте якщо запропонують, то вже маю ідеї щодо наступного воркшопу. Дякую всім за увагу і до наступних зустрічей, вони ще будуть.
16 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів