Дмитро Мелков, CIO MEGOGO в колонці для AIN.UA розповідає, якими принципами керувалися в MEGOGO, вибираючи програмні рішення, коли зіткнулися з обмеженнями обраних технологій.

Дмитро Мелков. Фото — Megogo.

Майже всі великі компанії сучасності починали з чогось малого — зі своєрідного «гаража», чи то підвалу у батьківському будинку, комірчини в офісному центрі чи кімнати в гуртожитку. Їх усіх поєднує важлива особливість — це були не state-of-the-art технології, а робочі рішення, які вирішували проблему. І тим паче ніхто не обирав модних мов програмування, якщо говорити про розробку. Задовольнялися тим, що знали, і що дозволяло закрити потреби у функціональності.

Цього достатньо для старту, але в кінцевому підсумку обране рішення доводиться допрацьовувати або переписувати повністю: брак потужності, складність масштабування, відсутність гнучкості — варіантів безліч. 

Космічна Одіссея 2001

А все починалося з моноліту, написаного на PHP та jQuery. Якщо говорити про архітектуру, то в 2011 році MEGOGO складався з великого веб-сайту та API для клієнтських додатків. Нічого неймовірного з погляду вибору мов програмування та рішень, але це працювало — для успішного запуску та перших кроків на ринку нам цього було достатньо. До того ж, вся команда, окрім CDN, складалася з 7 осіб, які фактично відповідали за все одночасно. Якщо з погляду бізнес-процесів це було неефективно, то з погляду розробки команда працювала оптимально.

Так тривало доти, доки не з’явилися клієнтські програми на Smart TV та iOS. Для них у ролі бекенда виступав той же PHP-моноліт, який був частиною сайту, що тільки посилило купу проблем.

Для початку, кодова база Legacy, над якою працювало кілька команд, і застарілі підходи ускладнювали розробку нової функціональності. І це посилювалося змішуванням процедурного та ООП підходів до розробки, що не рідкість у кодових базах після релізу 5-ї версії PHP. Ці обмеження не дозволяли нам досягти потрібної масштабованості та гнучкості. Тобто нам буквально доводилося додатково вигадувати, як реалізувати якісь досить банальні речі. Додайте сюди використання SVN для контролю версій та деплої вручну, а потім посипте зверху непередбачуваними релізами для смаку. Виходить не надто апетитно.

Наш Team Lead/Software Architect Дмитро Коваленко ділиться знаннями про вирішення проблеми: «Об’єктивно було зрозуміло, що всі проблеми не вирішити. Тому на ранньому етапі було вжито такі кроки:

  1. Виправили архітектурні помилки – перейшли до єдиного стилю розробки та використання сучасних рішень. Так з кодовою базою стало трохи легше працювати та підтримувати її.
  2. Мігрували наші репозиторії у GIT.
  3. Автоматизували «деплой».

Так ми плавно за два роки прийшли до впровадження Symfony. Моноліт нікуди не подівся, але фреймворк дав нам низку приємних бонусів. Кодову базу стало легше підтримувати, архітектура стала консистентною, а хороших фахівців з досвідом Symfony не складно знайти. Тож команда розробки також стала потроху зростати.

На цьому етапі у нас до моноліту підключився Android-додаток, і ми зіткнулися з новими проблемами. Команда розробки збільшилась до 10 осіб, які працювали в одному репозиторії одночасно. До того ж, на той момент у нас було вже 3 проекти: вебсайт, API та адмін. системи. Кожен зі своїм деплой-циклом, які не завжди вдавалося синхронізувати. Ну і основна проблема — за наші деякі рішення, наприклад, використовувати ORM для роботи з реляційною БД, ми платили продуктивністю застосунку. Також неможливо було жорстко обмежити кількість з’єднань з БД з додатків, а в PHP не було можливості працювати з пулом коннектів, тому кожен php-fpm-процес, що запускався, створював додаткові з’єднання на базу.

Для вирішення першої та другої проблем ми розділили репозиторій та сформували чіткі продуктові команди. А для третьої проблеми легкого і зрозумілого рішення, окрім як позбавитися ORM у випадках, коли не потрібні оптимально і точно написані запити, просто не було. Так розпочався наш шлях у бік мікросервісної архітектури та застосування «альтернативних» мов програмування.

Навіщо мікросервіси?

Очевидно, що рішення відмовитися від моноліту у бік мікросервісів не було ухвалено одномоментно. Перед цим нам потрібно було ґрунтовно зважити всі «за та проти», дослідити ринок технологій, подивитися на кількість фахівців у цікавих нам галузях. У результаті вдалося виділити для себе ряд особливостей (не завжди плюсів і мінусів) монолітної та мікросервісної архітектур.

Детальніше цю тему розкриває Дмитро Коваленко:

«Моноліт»

  • Якщо масштабувати, то все й одразу;
  • Проблеми, які можуть виникнути після деплою, перетворюються на «пошук голки у копиці сіна»;
  • Потрібна жорстка дисципліна у межах однієї кодової бази;
  • необхідно дотримуватися чітких меж сервісів;
  • Потрібно постійно стежити, щоб абстракції не «протікали» і не виходили за межі модуля/сервісу;
  • Складно змінювати архітектуру та пробувати нові технології та мови.

Мікросервіси

  • Простіше масштабувати, навіть якщо застосунок вже в продакшені;
  • Не страшні деплої, якщо не зламаний «контракт» між сервісами;
  • Можна використовувати будь-які відповідні технології, мови, фреймворки та архітектури;
  • Висока автономність команд».

Таким чином ми зрозуміли, що MSA підкуповує нас своєю гнучкістю та розподіленістю. Але це ще не означає, що настав час дробити моноліт. Залишалося вирішити ще кілька питань: по-перше, чи не будуть мікросервіси надмірними у нашому конкретному випадку; по-друге, як імплементувати мікросервіси на нашій платформі.

Наш моноліт став занадто великим і неповоротким, тому навіть з урахуванням усіх складнощів переходу та впровадження мікросервісів (тільки невеликий перелік: автоматизація розгортання, налаштування моніторингу, відмовостійкість, узгодженість між модулями) у довготривалій перспективі ми вигравали у продуктивності.

З архітектурою також не все просто. Але ми визначилися, що будемо використовувати підхід API Gateway, коли єдиний API перенаправляє клієнтські програми на потрібний мікросервіс.

Що з цього вийшло?

У 2014 році ми випустили першу версію API, до якої підключалися наші клієнти на Smart TV, iOS та Android. Веб-сайт megogo.net частково виступав у ролі бекенда, а окремим мікросервісом став сервіс електронної телепрограми (EPG v1).

У цей перехідний період потрібно було подолати нові перешкоди: проблеми з продуктивністю, викликані в тому числі обмеженнями PHP-бази, і безліччю серверів, кількість яких зростала в арифметичній прогресії. Тому розпочався наш незабутній пошук нових швидких та ефективних мов, які б дозволили вирішити наші проблеми.

Докладно про саму архітектуру ми розповідали у наших минулих публікаціях, зараз же сконцентруємось на складнощах.

Життя після PHP

У виборі відповідних технологій ми відхилилися від звичної типізації на користь потреб: високої швидкості та популярності. На нашій системі координат потрібні характеристики мали ряд мов програмування, включаючи Python, Scala, Java і Go. Як вибрати? Правильна відповідь — використовуйте мови, якими володієте найкраще. А якщо їх декілька? Потрібно провести вимірювання швидкості!

Тому команді розробки надійшло цікаве завдання: написати сервіс, який виконує кілька асинхронних запитів та повертає json/html. Звичайно, без кешування, тільки чистий замір продуктивності. Після вимірів з наших претендентів на першому місці опинився Java, відразу за ним Scala, а PHP десь у хвості. Go міг також стати гарним для нас варіантом, але у часи наших тестів був ще не такий популярний. Ми зупинилися на перших двох.

Графік замірів продуктивності:

  • PHP – ~200 rps (requests per second);
  • Ruby – ~50 rps;
  • Scala – ~2500 rps;
  • Java – ~4000 rps.

Обрати-то обрали, залишилося тільки реалізувати. За рік команда розробки, 30 осіб, включно з продуктовими командами, переписала REST API з нуля на Java. 

Крім сервісу електронної телепрограми EPG (Java) з’явився сервіс передплат (Java), паралельно виділився сервіс – білінг, який обробляє оплати користувачів (Scala). На Scala також переписали бекенд веб-сайту та сервіс відображення реклами.

Схема архітектури зразка 2015 року

Так стартував наш шлях у MSA. А це означало, що крім купи мов та фреймворків ми почали використовувати ще купу технологій та рішень для MSA. Детальніше розповість Діма Коваленко:

  • «Model: active or reactive: linkerd, kafka;
  • Load balancing / Retry / Circuit breaker: linkerd;
  • Caching: ehcache, memcache, aerospike;
  • Service discovery: consul, linkerd;
  • Distributed configuration management: consul;
  • Tracing, logs, debug: prometheus, jaeger, grafana, clickhouse;
  • Scaling up and down: kubernetes, AWS.

І цей список постійно змінюється та доповнюється!

У результаті ми плавно дійшли до теперішньої архітектури. API та частина сервісів написані на Java, ще частина — на Scala, у рекомендаційних системах та для збору аналітики використовуємо Python, а внутрішню систему адміністрування поступово переписуємо з PHP на React. Ще один сервіс, який відповідає за керування промокодами, написаний на Kotlin. А список використовуваних технологій включає модні Kubernetes, Kafka, Docker, Linkerd, MongoDB, Aerospike».

Схема сучасної архітектури MEGOGO

Чому ми навчилися за 10 років?

Масштабувати програми та платформу простіше, якщо розумієш вузькі місця. Наша найпопулярніша платформа, крім вебсайту, — Smart TV, тому зі зростанням кількості користувачів ми відокремили частину API, специфічних для додатку на розумних телевізорах, в окремий шлюз.

Наш найбільш навантажений клієнт — вбудовані (Embed) сторінки MEGOGO на партнерських сайтах. На піку у 2020 році кількість запитів від нього перевищувала 16 000 за секунду! Направляти такий потік на API як мінімум недалекоглядно, тому запити спочатку потрапляють на cache-сервер, з якого лише 10% вхідного трафіку перенаправляється на API.

На своєму досвіді ми знаємо, що регулярні перегляди архітектури та технологій — це завжди правильне рішення. І що іноді потрібно обговорювати обрані рішення та підходи не лише всередині команди, а й залучати зовнішніх консультантів та аудиторів, які зможуть подивитися з боку.

Вивчати кращі практики в індустрії та популярні технологічні рішення не соромно. Це економить час, гроші та ресурси, тому що багато проектів стикаються зі схожими проблемами.

Регулярно виділяйте 20% часу на тестування, експерименти та опрацювання нових технологій, підходів та рішень. Принцип Парето ніхто не скасовував, у майбутньому 20% зусиль перетворяться на 80% результату.

Автор: Дмитро Мелков, CIO MEGOGO