Почему вы должны применить принцип единой ответственности к бессерверным системам

1660424435 pochemu vy dolzhny primenit princzip edinoj otvetstvennosti k besservernym sistemam

Янь Цуй

Забавный момент (в 38:50) случился во время сессии Тима Брея (SRV306) на re:invent 2017. Тем спросил аудиторию, есть ли у нас много одноцелевых функций или меньше монолитных функций, и мнения разделились одинаково. .

Это был момент, который подверг сомнению мою веру, поскольку я был воспитан на принципах SOLID.

  • Принцип единой ответственности
  • Открытый/закрытый принцип
  • Принцип подстановки Лескова
  • Принцип разделения интерфейса
  • Принцип инверсии зависимостей

Я долгое время считал, что после Принцип единой ответственности (SRP) – это беспроигрышное дело.

Это побуждало к более детальному изучению аргументов обеих сторон.

Полное раскрытие: Я предвзято в этой дискуссии. Если вы обнаружили недостатки в моих мыслях или не согласны с моими взглядами, укажите это в комментариях.

Под «монолитными функциями» я подразумеваю функции, имеющие внутреннюю логику разветвления. Эти функции могут выполнять одну из нескольких вещей на основе события вызова.

1*lIL-txZJ5iBrg9DDf6-nww

К примеру, вы можете иметь одну функцию, которая будет обрабатывать все конечные точки для API. Функция будет выполнять другое действие на основе path и method параметры

module.exports.handler = (event, context, cb) => {   const path = event.path;   const method = event.httpMethod; 
  if (path === '/user' && method === 'GET') {     .. // get user   } else if (path === '/user' && method === 'DELETE') {     .. // delete user   } else if (path === '/user' && method === 'POST') {     .. // create user   } else if {    .. // other endpoints & methods   }}

Вы не можете рационально рассуждать и сравнивать решения, не поняв сначала проблему и то, какие качества наиболее предпочтительны в решении.

И когда я слышу жалобы вроде того, что «трудно управлять таким количеством функций», я задаюсь вопросом, что это значит управлять тянет за собой?

  • Это для поиска конкретных функций, которые вы ищете?
  • Это для того, чтобы узнать, какие функции у вас есть?
  • Становится ли это проблемой, когда у вас есть 10 или 100 функций?
  • Становится ли это проблемой только тогда, когда над ними работает больше разработчиков, чем вы можете отследить?

Исходя из собственного опыта, проблема не связана с тем, какие функции у нас есть. Скорее, речь идет о том, какими функциями и возможностями мы обладаем с помощью этих функций.

В конце концов, функция Lambda, как контейнер Docker, является каналом для предоставления некоторых бизнес-функций или возможностей.

Вы бы не спрашивали «Есть ли у нас а get-user-by-facebook-id функция?» поскольку вам понадобится знать, как была вызвана функция, даже не зная, существует ли такая возможность и зафиксирована ли она лямбда-функцией. Вместо этого вы, наверное, спросили бы, «Есть ли у нас функция Lambda, которая может найти пользователя по его/ее идентификатору в Facebook?»

Итак, настоящая проблема заключается в том, что, учитывая, что у нас есть сложная система, состоящая из многих функций и возможностей, поддерживаемых многими командами разработчиков, как мы организуем эти функции и возможности в лямбда-функции, чтобы они были оптимизированы для:

  • проявленность: как узнать, какие функции и возможности есть в нашей системе?
  • отладка: как быстро определить и найти код, который мне нужно просмотреть для устранения проблемы? Например, в журналах системы X есть ошибки, где я могу найти соответствующий код, чтобы начать отладку системы?
  • масштабирование команды: как минимизировать трение и увеличить команду инженеров, сохраняя код?

Ниже перечислены качества, которые для меня самые важные. Имея эти знания, я могу сравнить разные подходы и увидеть, какой есть лучше всего подходит для меня.

1*OG159w6MLTOQnLL6ei18lw

Вы можете заботиться о разных качествах. К примеру, вы можете не заботиться об увеличении команды, но стоимость является для вас важным фактором. Какими бы они ни были, полезно сделать эти цели дизайна четкими. Вы также должны убедиться, что ваши команды имеют доступ к ним и понимают их.

Проявленность

Отсутствие видимости не новая проблема. По словам Саймона Уордли, это достаточно распространено как в правительстве, так и в частном секторе. Большинству организаций не хватает систематического способа, посредством которого команды могли бы делиться и узнавать о работе друг друга.

1*xqxvFs3jRv_gc9eePzWf2g
благодаря публикациям Саймона Вордли в Twitter

Как было сказано ранее, обнаружение состоит в том, чтобы узнать, какие возможности доступны с помощью ваших функций. Знать какие функции у вас есть недостаточно.

1*6FC_6slSGi3GyQP_5zMLLw
Не спрашивайте, какие у вас функции, скорее спросите, что ваши функции могут выполнять.

Аргумент, который я часто слышу в пользу монолитных функций, заключается в том, что они уменьшают количество функций, что облегчает управление ими.

Снаружи кажется, что это имеет смысл. Но чем больше я думаю об этом, тем больше кажется, что аргумент ошибочен. Количество функций будет только препятствием, если мы попытаемся управлять ими вручную, а не использовать уже доступные нам инструменты.

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

1*P9R8YbGTO_zVQH3Owjbrig

Например, бессерверный фреймворк предусматривает простое соглашение об именовании {service}-{stage}-{function}. Эта простая сделка позволяет легко находить связанные функции по префиксу. Если я хочу найти все функции, которые являются частью a user API, я могу это сделать с помощью поиска user-api.

1*xNLVK6_Jq7MVZd69Yn3NDg

С помощью меток мы можем каталогизировать функции в нескольких измерениях. Например, мы можем каталогизировать, используя среду, название функции, источник события, автора и т.д.

1*ck-QETFR0AMVV0OgHP2usQ
По умолчанию бессерверная структура добавляет тег STAGE ко всем вашим функциям. Вы также можете добавлять собственные теги. документацию по добавлению меток.
1*jAtLpmlp2yjQ4cT3JwUKWQ
Консоль управления Lambda также предоставляет вам удобный нисходящий список доступных значений, когда вы пытаетесь искать по тегу.

Если у вас есть приблизительное представление о том, что вы ищете, то количество функций не препятствует вашей способности обнаружить, что там есть.

С одноцелевыми функциями, возможности user-api сразу видно. Из соответствующих функций я вижу, что у меня есть базовые возможности CRUD, поскольку для каждой есть соответствующие функции.

1*C22Wu2wC8uROThTfwyn7iQ
Я вижу, какие возможности у меня есть как часть набора функций, которые составляют функцию user-api.

Однако с монолитной функцией все не так-то просто. Есть только одна функция, но что эта функция может сделать? Мне придется либо самому посмотреть код, либо проконсультироваться с автором функции. Для меня это делает плохую видимость.

1*o9wXc2rndRZm2hO0ggevtA

Поэтому я отношу монолитный подход к видимости.

1*bXYf0mExyAPrzHuA3wMz2A

Но наличие большего количества функций означает больше страниц для прокрутки. Это может быть трудоемким, если вы просто хотите просмотреть и увидеть, какие функции есть.

1*1Xjwc62Up78CfUs3qwYECg

Хотя, по моему опыту, это никогда не было проблемой как таковой. Благодаря Бессерверный согласно именованию фреймворка, все связанные функции расположены около друг друга. На самом деле очень приятно видеть, что может делать каждая группа функций, вместо того чтобы думать, что происходит внутри монолитной функции.

Но это может быть больно прокручивать все, когда у вас есть тысячи функций. Итак, я собираюсь наказать за это одноцелевые функции.

Однако на таком уровне сложности размещения дополнительных возможностей в каждой функции лишь усложнит понимание системы. Скажем, у вас есть тысяча функций и вы с первого взгляда знаете, что делает каждая. Не было ли проще заменить их сотней функций, но вы не можете сказать, что каждая из них делает?

1*xGGFxDFUXcRHNDjopc23eA

Настройка

Для отладки релевантный вопрос заключается в том, меньше ли количество функций облегчает идентификацию и локализацию ошибки.

По моему опыту, путь от ошибки к соответствующей функции и репозиторию одинаков, независимо от того, выполняет функция одно или многое.

1*qV9A5t6nXBmulssjGXzfRw

Разница заключается в том, как найти соответствующий код в репозитории для исследуемых проблем.

Монолитная функция имеет более разветвленную логику. Таким образом, нужно было бы больше когнитивных усилий, чтобы следовать коду, имеющему отношение к рассматриваемой проблеме.

Для этого я немного сбавлю монолитные функции. Конечно, мы говорим о минимальной разнице, поэтому штраф также минимален.

1*7BfnhHIlQx0KEqnFIxQa4A

Масштабирование

В начале микросервисов одним из аргументов в пользу микросервисов было то, что они упрощают масштабирование.

Но это не так!

Если вы знаете, как масштабировать систему, вы можете масштабировать монолит так же легко, как и микросервис.

Я говорю это как человек, построивший монолитные серверные системы для игр, которые имели миллион ежедневных активных пользователей (DAU). Суперячейкасоздатель самых кассовых игр, таких как Война кланов и Clash Royale, имеют более чем 100 миллионов DAU. Все базовые системы для этих игр являются монолитами. Суперячейка нет проблем с масштабированием этих систем.

1*uXFIo5_szcKnh0kd6m3Vxg

Однако такие технические гиганты, как Amazon и Google, научили нас, что микросервисы упрощают масштабирование в другом измерении — наша команда инженеров.

Этот стиль архитектуры позволяет нам создавать границы внутри нашей системы вокруг функций и возможностей. Это позволяет нашим командам инженеров масштабировать сложность того, что они создают, поскольку они могут легче создавать на основе работы, созданной другими.

Возьмем в качестве примера облачное хранилище данных Google. Инженеры этой команды смогли создать сложную услугу, построив ее на основе многих уровней услуг. Каждый слой обеспечивает мощные абстракции, которые могут использовать следующий уровень.

1*cyUwWcGZkfpw4tzlrS8muQ
http://bit.ly/2CQx3C4

Эти границы дают нам большее разделение труда. Это позволяет большему количеству инженеров работать над системой, предоставляя им зоны, где они могут работать в относительной изоляции. Таким образом, они не спотыкаются друг о друга из-за конфликтов слияния, проблем интеграции и т.д.

Майкл Найгард также написал хорошую статью, объясняющую это преимущество с другой стороны: эти границы и изоляция помогают нам снизить накладные расходы на обмен ментальными моделями.

«Если у вас высокий уровень слаженности и много людей, то команда в целом двигается медленнее… Речь идет о сокращение накладные расходы на обмен умственными моделями.”

— Майкл Найгард

Наличие большого количества одноцелевых функций, по-видимому, вершиной такого распределения задач. Вы немного теряете это разделение, когда переходите к монолитным функциям. Хотя на практике вы, вероятно, не будет столько разработчиков, которые работают над одним проектом, чтобы вы чувствовали боль.

Ограничение выполнения определенной функции помогает также ограничить сложность функции. Чтобы сделать что-то более сложное, вы должны скомпоновать эти простые функции вместе с другими средствами, например, с помощью AWS Шаговые функции.

Я отмечаю монолитные функции из-за утраты определенного разделения труда и повышения уровня сложности функции.

1*GNu9RlaxpUhdyTRwyLzozQ

Вывод

На основе критерии, которые есть важно для меня, наличие многих одноцелевых функций является предпочтительным путем. Но я не считаю это жестким правилом.

Как и все остальные, я прихожу с набором склонностей и предубеждений, сформированных на основе моего опыта, который скорее всего не совсем отражает ваш. Я не прошу вас соглашаться со мной. Хотя я надеюсь, что вы цените процесс определения того, что вам важно, чтобы вы могли найти правильный подход для себя.

Но о чем холодные запуски? Разве монолитные функции не помогут вам снизить количество холодных запусков?

Краткий ответ: нет, они не помогают вам при холодном запуске каким-либо значимым образом. Это тоже неправильное место для оптимизации холодного запуска. Если вас интересует удлиненная версия этого ответа, пожалуйста, прочтите мое другое сообщение здесь.

И, наконец, наличие меньших площадей поверхности с одноцелевыми функциями уменьшает поверхность атаки. Вы можете предоставить каждой функции точные разрешения, которые ей нужны, и ничего больше. Это важное, но часто недооцененное преимущество одноцелевых функций.

Добавить комментарий

Ваш адрес email не будет опубликован.