Upgrade to Pro — share decks privately, control downloads, hide ads and more …

«Тактика распила PHP-монолита» — Лакосников Павел, Авито

De18318c9ff86ea93435effe50a43c4b?s=47 Badoo Tech
February 15, 2020

«Тактика распила PHP-монолита» — Лакосников Павел, Авито

«Последние три года мы в Авито активно разделяем PHP-монолит на микросервисы. В процессе нашли много устаревших продуктовых и технологических решений — неактуальные хранилища, слишком «толстые» ответы, неиспользуемые методы API.

В докладе расскажу, как мы избавлялись от легаси:‌ выносили словари и другую статику, выделяли интерфейсы, упрощали иерархию наследования и совершенствовали покрытие тестами. А ещё — как улучшаем то, что пока осталось в монолите».

De18318c9ff86ea93435effe50a43c4b?s=128

Badoo Tech

February 15, 2020
Tweet

More Decks by Badoo Tech

Other Decks in Programming

Transcript

  1. Павел Лакосников Senior engineer, юнит Antimonolith Тактика распила монолита

  2. !2 Из монолита в микросервисы

  3. Монолит 3 года назад !3

  4. !4 Как выглядел монолит 3 года назад: 2.6к файлов и

    200к строк PHP 1.5к html файлов 1к+ файлов js, lua, python, perl ServiceRegistry + Global как основные способ работы с базой Разветвлённая многоуровневая система наследования
  5. !5 Как выглядел монолит 3 года назад: 2.6к файлов и

    200к строк PHP 1.5к html файлов 1к+ файлов js, lua, python, perl ServiceRegistry + Global как основные способ работы с базой Разветвлённая многоуровневая система наследования
  6. Он работал! !6

  7. !7 Это был местами читаемый и неплохой код

  8. !8 Он был надёжен

  9. !9 Он был надёжен,
 если работал

  10. !10 Он работал очень быстро

  11. !11 Чем же он нам мешал?

  12. !12 Не позволял быть гибкими

  13. !13 Не позволял быть гибкими Не позволял командам быстро внедрять

    изменения, не вызывая «эффект лавины»
  14. !14 Не позволял быть гибкими Не позволял командам быстро внедрять

    изменения, не вызывая «эффект лавины» Не позволял экспериментировать
  15. !15 Не позволял быть гибкими Не позволял командам быстро внедрять

    изменения, не вызывая «эффект лавины» Не позволял экспериментировать Гигантскими тяжелыми моделями
  16. !16 Начинаем распиливать…

  17. Вендоризация !17

  18. !18 Как выглядел код в монолите

  19. !19 Пример сессий: Роутеры

  20. !20 Пример сессий: Бэкофис

  21. !21 Пример сессий: Авторизация

  22. !22 Пример сессий: Мессенджер

  23. !23 Пример сессий: Наследники

  24. !24 Вендоризация 1. Собираем код в отдельный пакет 2. Прогоняем

    статический анализатор 3. Пишем тесты 4. Подключаем к монолиту как внешнюю библиотеку
  25. !25 Зачем? 1. Контроль за изменениями 2. Изоляция всех внешних

    зависимостей 3. Одинаковое поведение у всех потребителей
  26. !26 Как не надо делать

  27. !27 Не писать тесты на перенесённый код

  28. !28 Просто копировать файлики

  29. !29 Переносить все в одну библиотеку

  30. Вынос словарей !30

  31. !31 Что именно Список городов Список районов Список категорий Список

    микрокатегорий
  32. !32 Зачем? Это корневые зависимости, нужные не только в монолите,

    но и в большинстве сервисов
  33. !33 Зачем? Это корневые зависимости, нужные не только в монолите,

    но и в большинстве сервисов Фиксация констант, интерфейсов
  34. !34 Зачем? Это корневые зависимости, нужные не только в монолите,

    но и в большинстве сервисов Фиксация констант, интерфейсов Контроль за их изменением
  35. !35 Зачем? Это корневые зависимости, нужные не только в монолите,

    но и в большинстве сервисов Фиксация констант, интерфейсов Контроль за их изменением Доступность для любых сервисов
  36. !36 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы

  37. !37 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы

    Отделили базу данных, хранящих словарные сущности
  38. !38 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы

    Отделили базу данных, хранящих словарные сущности Сделали микросервис, способный отдавать словарные данные в куче форматов
  39. !39 Как именно? Вендоризовали интерфейсы всех словарных сущностей, все константы

    Отделили базу данных, хранящих словарные сущности Сделали микросервис, способный отдавать словарные данные в куче форматов Доработали сборку монолита, добавив этап сборки словарей
  40. !40

  41. Уменьшение глубины наследования !41

  42. !42 Глубина наследования классов достигала 8 уровней

  43. !43 Controller_Site_Profile_Settings_Ru

  44. !44 Расширение ответа по цепочке от наследников к родителю

  45. !45 На вход в метод отрисовки объявления $expandedItem = [

    'id' => $itemId, 'category' => [ 'id' => $itemCategory->id, 'name' => $itemCategory->name ] ];
  46. !46 На выходе из цепочки 6 килобайт полезных данных 150

    различных полей
  47. !47 В самом конце … if ($item->isOnModerate()) { return $this->notFound();

    } ];
  48. !48 Проблемы Трудно рефакторить Легко задеть чужую функциональность Избыточность данных

  49. !49 Что вообще происходит?

  50. !50 Как именно? Диаграммы вызовов в разрезе запроса

  51. !51 Как именно? Диаграммы вызовов в разрезе запроса Вдумчивый и

    внимательный рефакторинг
  52. !52 NO SILVER BULLET

  53. Классы вместо массивов !53

  54. !54 Что именно? Многие сущности представлялись в коде как большие

    массивы с данными
  55. !55 Что именно? Многие сущности представлялись в коде как большие

    массивы с данными Эти массивы передаются из метода в метод, по цепочке
  56. !56 Что именно? Многие сущности представлялись в коде как большие

    массивы с данными Эти массивы передаются из метода в метод, по цепочке В процессе дополняются, расширяются и декорируют
  57. !57 Зачем? Больше типизации => меньше ошибок

  58. !58 Зачем? Больше типизации => меньше ошибок Понимание, какие поля

    и где используются
  59. !59 Зачем? Больше типизации => меньше ошибок Понимание, какие поля

    и где используются Удаление неиспользуемых или устаревших полей
  60. !60 Как именно? Создаем простейшую модель Item {id, title}

  61. !61 Как именно? Создаем простейшую модель Item {id, title}

  62. !62 Как именно? Постепенно расширяем её, создавая новые типы данных

    (Price, Contacts) и сокращая объем $data
  63. Сделали маленькие модели !63

  64. !64 Что именно? На прошлом этапе у нас получились большие

    модели
  65. !65 Объявление в поиске

  66. !66 Страница объявления

  67. !67 Объявление в чужом профиле

  68. !68 Объявление в своём профиле

  69. !69 Изначально везде была одна модель

  70. !70 Проблемы Огромная холостая нагрузка на сеть

  71. !71 Проблемы Огромная холостая нагрузка на сеть Низкая надежность

  72. !72 Проблемы Огромная холостая нагрузка на сеть Низкая надежность Сложность

    тестирования
  73. !73 Проблемы Огромная холостая нагрузка на сеть Низкая надежность Сложность

    тестирования Невозможность создать микросервис, отвечающий только за часть свойств этой модели
  74. !74 Как именно? Выделили десятки интерфейсов из большой модели

  75. !75 Как именно? Выделили десятки интерфейсов из большой модели Разбили

    одну большую модель на множество маленьких
  76. !76 Как именно? Выделили десятки интерфейсов из большой модели Разбили

    одну большую модель на множество маленьких Сделали отдельные фабрики для каждой из них
  77. Перешли на DataMappper вместо ORM !77

  78. !78 Что именно? Вызовы хранимок на получение данных из самых

    разных места монолита
  79. !79 Что именно? Вызовы хранимок на получение данных из самых

    разных места монолита Сохранение моделей в отличном от создания месте
  80. !80 Что именно? Вызовы хранимок на получение данных из самых

    разных места монолита Сохранение моделей в отличном от создания месте Разные алиасы у полей, и способы получения данных
  81. !81 Достаем item[100] из базы

  82. !82 Достаем item[100] из базы

  83. !83 Достаем item[100] из базы

  84. !84 Проблемы Большая глубина наследования на классах занимающихся работой с

    моделью
  85. !85 Проблемы Большая глубина наследования на классах, занимающихся работой с

    моделью Большое количество входных похожих точек, вызывающих работу с моделью
  86. !86 Проблемы Большая глубина наследования на классах занимающихся работой с

    моделью Большое количество входных похожих точек, вызывающих работу с моделью Неконсистентность вызовов
  87. !87 Как именно? Переход на dataMapper модель

  88. !88 Как именно? Переход на dataMapper модель Подстройка публичного интерфейса

    под нужды конкретных потребителей
  89. !89 Как именно? Переход на dataMapper модель Подстройка публичного интерфейса

    под нужды конкретных потребителей Инкапсулюяция всей логики базы связанной с сущностью в одном месте
  90. Отказались от поддержки старых приложений !90

  91. !91 Что именно? Большой хвост из старых версий мобильных приложений

  92. !92 Что именно? Большой хвост из старых версий мобильных приложений

    Большое десктоп-апи, которые не чистили раньше
  93. !93 Проблемы Старые методы ожидали очень большие модели данных, избыточные

    для их работы
  94. !94 Проблемы Старые методы ожидали очень большие модели данных, избыточные

    для их работы Часть запросов использовались исключительно ботами
  95. !95 Проблемы Старые методы ожидали очень большие модели данных, избыточные

    для их работы Часть запросов использовались исключительно ботами Часть запросов были «мертвыми»
  96. !96 Проблемы Старые методы ожидали очень большие модели данных, избыточные

    для их работы Часть запросов использовались исключительно ботами Часть запросов были «мертвыми» Осложняли рефакторинг
  97. !97 Как именно? Сделали метрики по использованию API

  98. !98

  99. !99 Как именно? Сделали метрики по использованию API Сверились c

    ними
  100. !100

  101. !101 Как именно? Сделали метрики по использованию API Сверились c

    ними Отказались от поддержки версий приложений с минорным трафиком
  102. !102 Начали делать это регулярно

  103. Перешли на Redis !103

  104. !104 Что именно? В проекте было несколько коммунальных мемкешей Использовались

    они для кратковременного кеширования
  105. !105 Проблемы Перетирали ключи в разных потребителях

  106. !106 Проблемы Перетирали ключи в разных потребителях Автоматически конвертит классы

    через php_serialize (круто, когда класс переименовали а кэш нет)
  107. !107 Проблемы Перетирали ключи в разных потребителях Автоматически конвертит классы

    через php_serialize (круто, когда класс переименовали а кэш нет) Не перистентен
  108. !108 Проблемы Перетирали ключи в разных потребителях Автоматически конвертит классы

    через php_serialize (круто, когда класс переименовали а кэш нет) Не перистентен Общие политики вытеснения для всех
  109. !109 Как именно? Плавно заменили несколько мемкешей на большую пачку

    персистентных Redis
  110. !110 Как именно? Плавно заменили несколько мемкешей на большую пачку

    персистентных Redis Выделили отдельные обертки под отдельные Redis
  111. !111 Как именно? Плавно заменили несколько мемкешей на большую пачку

    персистентных Redis Выделили отдельные обертки под отдельные Redis Разделили потребителей каждый по своему Redis’у
  112. !112 Их еще и из монолита легче вытащит

  113. Что же стало с монолитом? !113

  114. !114 Монолит не распилен

  115. !115 Он даже увеличился

  116. !116 13к файлов PHP 554к строк PHP

  117. !117 ≈ 300микросервисов ≈ 1500 репозиториев

  118. !118 Перешли на 
 PHP 7.4

  119. !119 Что изменилось

  120. !120 implode (string $glue , array $pieces ) Раньше порядок

    аргументов не имел значения Было implode (string $glue, array $pieces): string implode (array $pieces, string $glue): string Стало implode (string $glue, array $pieces): string
  121. !121 Внедрили DI

  122. !122 Добавили литеры Php-stan Php-cs Route-checker Сrontab-checker Проверка схемы ответов

    API Линтер на examples ответов API Валидация контраков
  123. !123 Добавили литеры Php-stan Php-cs Route-checker Сrontab-checker Проверка схемы ответов

    API Линтер на examples ответов API Валидация контраков
  124. !124 У нас появился service-price-formatter

  125. !125 Сервис форматирующий цену

  126. !126 Да, это stateless сервис, просто форматирующий цену

  127. !127 Он утилизирует 40 ядер

  128. !128 Мы его удалили

  129. Декомпозируй правильно! !129

  130. Почти конец !130

  131. !131 Чтобы распилить монолит…

  132. !132 Не делайте price-formatter

  133. !133 Избавьтесь от Dependency Hell

  134. !134 Увеличьте покрытие тестами и литерами

  135. !135 Откажитесь от ORM, 
 ServiceRegistry и Global’ов

  136. Павел Лакосников Senior engineer, юнит Antimonolith facebook: https://www.facebook.com/shaman.s.bubnom telegram: shaman_s_bubom