ProHoster > Блог > Администрирование > Программная архитектура и проектирование систем: общая картина и путеводитель по ресурсам
Программная архитектура и проектирование систем: общая картина и путеводитель по ресурсам
Здравствуйте, коллеги.
Сегодня на ваш суд предлагается перевод статьи Тугберка Угурлу (Tugberk Ugurlu), который взялся в сравнительно небольшом объеме изложить принципы проектирования современных софтверных систем. Вот что автор сообщает о себе в сухом остатке:
Поскольку решительно невозможно охватить в хабростатье такую колоссальную тему, как архитектурные паттерны + паттерны проектирования по состоянию на 2019 год, рекомендуем не только сам текст господина Уруглу, но и многочисленные ссылки, которые он в нем любезно расставил. Если вам понравится — опубликуем и более узкоспециальный текст о проектировании распределенных систем.
Если вам никогда не приходилось сталкиваться с такими вызовами, как проектирование софтверной системы с нуля, то, приступая к такой работе, иногда даже не понятно, с чего начать. Я считаю, что сначала требуется очертить границы, чтобы более-менее уверенно представлять, что именно вы собираетесь спроектировать, а затем – закатать рукава и работать, не выходя за эти границы. В качестве отправной точки можно взять какой-нибудь продукт или сервис (в идеале – такой, что вам очень нравится) и разобраться в его реализации. Возможно, вы поразитесь, насколько просто выглядит данный продукт, и какая огромная сложность в нем на самом деле скрыта. Не забывайте: простое – обычно сложное, и это нормально.
Думаю, наилучший совет, который я могу дать тем, кто приступает к проектированию системы, таков: не допускайте никаких допущений! С самого начала нужно конкретизировать факты, известные об этой системе, и связанные с нею ожидания. Вот несколько хороших вопросов, ответы на которые помогут вам приступить к проектированию:
Какова проблема, которую мы пытаемся решить?
Каково пиковое количество пользователей, которые будут взаимодействовать с нашей системой?
Какие паттерны записи и считывания данных у нас будут использоваться?
Каковы ожидаемые случаи отказов, как мы собираемся с ними справляться?
Какие ожидания предъявляются к согласованности и доступности системы?
Приходится ли учитывать при работе какие-либо требования, связанные с внешней проверкой и регламентацией?
Какие разновидности конфиденциальных данных мы собираемся хранить?
Это всего несколько вопросов, которые пригодились при работе как мне, так и тем командам, в которых мне довелось участвовать за годы профессиональной деятельности. Если вам известны ответы на эти вопросы (и на любые другие, релевантные в том контексте, в котором вам приходится работать), то можно постепенно углубляться в технические детали задачи.
Задаем исходный уровень
Что я понимаю здесь под «исходным уровнем» (baseline)? Собственно, в наши времена большинство проблем в софтверной индустрии «можно» решить при помощи уже имеющихся методов и технологий. Соответственно, ориентируясь в этом ландшафте, вы получаете определенную фору, сталкиваясь с задачами, которые кому-то приходилось решать до вас. Не забываем, что программы пишутся для решения проблем бизнеса и пользователей, поэтому мы стремимся решить задачу самым прямолинейным и простым (с точки зрения пользователя) способом. Почему об этом необходимо помнить? Может быть, вам в вашей системе координат нравится подыскивать для всех задач уникальные решения, так как вы считаете, «какой же я программист, если повсюду следую паттернам»? На самом деле, искусство здесь заключается в принятии решений о том, где и что делать. Разумеется, любому из нас время от времени приходится сталкиваться с уникальными проблемами, каждая из которых – настоящий вызов. Однако, если наш исходный уровень четко очерчен, то мы знаем, на что тратить силы: на поиск готовых вариантов решения поставленной перед нами задачи, либо на ее дальнейшее изучение и более глубокое понимание.
Думаю, мне удалось вас убедить, что, если специалист уверенно разбирается в том, какова архитектурная составляющая некоторых замечательных софтверных систем, то эти знания будут незаменимы для овладения искусством архитектора и наработки солидного базиса в этой сфере.
Хорошо, так с чего же начать? У Донна Мартина есть репозиторий на GitHub под названием system-design-primer, по материалам которого вы сможете научиться проектированию крупномасштабных систем, а также подготовиться к собеседованиям на эту тему. В репозитории есть раздел с примерами реальных архитектур, где, в частности, рассмотрено, как подходят к дизайну своих систем некоторые хорошо известные компании, например, Twitter, Uber, т.д.
Однако, прежде чем переходить к этому материалу, давайте подробнее разберемся с наиболее важными архитектурными вызовами, с которыми приходится сталкиваться на практике. Это важно, поскольку приходится конкретизировать МНОЖЕСТВО аспектов неподатливой и многогранной проблемы, а затем решать ее в рамках регламентации, действующей в данной системе. Джексон Габбард, бывший сотрудник Facebook, записал 50-минутное видео о собеседованиях, посвященных проектированию систем, где поделился собственным опытом по отсмотру сотен соискателей. Притом, что видео выраженно касается проектирования больших систем и критериев успешности, важных при подыскивании кандидата на такую позицию, оно, все-таки, послужит исчерпывающим ресурсом о том, какие вещи наиболее важны при проектировании систем. Также предлагаю краткое содержание этого видео.
Нарабатывайте знания о хранении и извлечении данных
Как правило, ваше решение о том, как вы будете долговременно хранить и выдавать ваши данные, критически сказывается на производительности системы. Следовательно, вы должны прежде всего понимать ожидаемые характеристики записи и считывания данных в вашей системе. Затем нужно уметь оценивать эти показатели и делать выбор, исходя из сделанных оценок. Однако, эффективно справляться с этой работой вы сможете лишь в случае, если разбираетесь в существующих паттернах хранения данных. В принципе, это подразумевает уверенные знания, связанные с подбором базы данных.
Базы данных можно считать структурами данных, которым присуща исключительная масштабируемость и долговечность. Поэтому знание структур данных должно весьма пригодится вам и при выборе той или иной базы данных. Например, Redis – это сервер структур данных, поддерживающий различные виды значений. Он позволяет работать с такими структурами данных как списки и множества, считывать данные при помощи общеизвестных алгоритмов, например, LRU, организуя такую работу в долговечном и высокодоступном стиле.
Когда вы в достаточной степени сориентируйтесь в различных паттернах хранения данных – переходите к изучению согласованности и доступности данных. Первым делом вам потребуется усвоить CAP-теорему хотя бы в общих чертах, а затем отшлифовать эти знания, подробнее рассмотрев устоявшиеся паттерны согласованности и доступности. Таким образом вы наработаете кругозор в этой области и поймете, что считывание и запись данных – это на самом деле две очень разные проблемы, и с каждой из них сопряжены свои особые вызовы. Вооружившись несколькими паттернами обеспечения согласованности и доступности, вы сможете значительно нарастить производительность системы, при этом гарантировав бесперебойную подачу данных в ваши приложения.
Наконец, завершая разговор о вопросах хранения данных, следует упомянуть и о кэшировании. Должно ли оно выполняться одновременно на клиенте и на сервере? Какие данные будут находиться у вас в кэше? И почему? Как вы организуете инвалидацию кэша? Будет ли она производиться регулярно, через определенные промежутки времени? Если да – то как часто? Проработку этих тем рекомендую начать со следующего раздела вышеупомянутого букваря по проектированию систем.
Коммуникационные паттерны
Системы состоят из различных компонентов; это могут быть как разные процессы, работающие внутри одного и того же физического узла, так и различные машины, действующие в разных частях вашей сети. Некоторые из этих ресурсов в пределах вашей сети могут быть приватными, но другие должны быть публичными и открытыми для потребителей, обращающихся к ним извне.
Необходимо обеспечить коммуникацию этих ресурсов друг с другом, а также обмен информацией между всей системой и внешним миром. В контексте проектирования систем здесь мы, опять же, сталкиваемся с набором новых уникальных вызовов. Разбираемся, чем могут быть полезны асинхронные потоки задач, и какие разнообразные паттерны коммуникации есть в наличии.
При организации коммуникации с внешним миром всегда очень важна безопасность, к обеспечению которой также нужно подходить со всей серьезностью и заниматься ею активно.
Распределение соединений
Не уверен, что вынесение этой темы в самостоятельный раздел всем покажется оправданным. Тем не менее, развернуто изложу здесь эту концепцию, причем, считаю, что материал этого раздела точнее всего описывается именно термином «распределение соединений» (connection distribution).
Системы формируются путем правильного соединения множества компонентов, и их коммуникация друг с другом зачастую организуется на базе устоявшихся протоколов, например, TCP и UDP. Однако, этих протоколов как таковых зачастую недостаточно для удовлетворения всех нужд современных систем, которые часто эксплуатируются под высокой нагрузкой, а также сильно зависят от потребностей пользователей. Часто бывает необходимо изыскивать способы распределения соединений, чтобы справляться с такими высокими нагрузками в системе.
В основе такого распределения лежит хорошо известная система доменных имен (DNS). Такая система позволяет преобразовать доменное имя, например, взвешенный циклический алгоритм (weighted round robin) и методы на основе задержек, помогающие распределять нагрузку.
Балансировка нагрузки принципиально важна, и практически любая крупная система в Интернете, с которой нам сегодня приходится иметь дело, расположена за одним или несколькими балансировщиками нагрузки. Балансировщики нагрузки помогают распределять клиентские запросы по множеству доступных инстансов. Балансировщики нагрузки бывают как аппаратными, так и программными, однако, на практике чаще приходится иметь дело с программными, например с HAProxy и ELB. Обратные прокси концептуально также очень похожи на балансировщики нагрузки, хотя, между первыми и вторыми есть ряд отчетливых различий. Эти различия обязательно нужно учитывать, проектируя систему с учетом ваших потребностей.
Также следует знать о сетях доставки контента (CDN). CDN – это глобальная распределенная сеть прокси-серверов, доставляющая информацию из тех узлов, которые географически расположены ближе к конкретному пользователю. Сети CDN предпочтительнее использовать, если вы работаете со статическими файлами, написанными на JavaScript, CSS и HTML. Кроме того, сегодня распространены такие облачные сервисы, в которых предоставляются диспетчеры трафика, например, Azure Traffic Manager, обеспечивающие вам глобальное распределение и сниженные задержки при работе с динамическим контентом. Однако, такие сервисы обычно полезны в тех случаях, когда приходится работать с веб-сервисами без сохранения состояния.
Поговорим о бизнес-логике. Структурирование бизнес-логики, потоков задач и компонентов
Итак, мы успели обсудить разнообразные инфраструктурные аспекты системы. Скорее всего, пользователь даже не задумывается обо всех этих элементах вашей системы и, честно говоря, совершенно по их поводу не парится. Пользователя интересует, каково взаимодействовать с вашей системой, чего можно добиться, поступив вот так, а также каким образом система выполняет пользовательские команды, что и как делает с пользовательскими данными.
Как понятно из названия данной статьи, я собирался рассказать в ней о софтверной архитектуре и проектировании систем. Соответственно, я не планировал освещать паттерны проектирования ПО, описывающие, как создаются программные компоненты. Однако, чем больше я об этом размышляю, тем больше мне кажется, что граница между паттернами проектирования ПО и архитектурными паттернами очень размыта, и две эти концепции тесно связаны. Возьмем, к примеру, регистрацию событий (event sourcing). Стоит вам взять на вооружение этот архитектурный паттерн – и он повлияет практически на все аспекты вашей системы: долговременное хранение данных, уровень согласованности, принятый в вашей системе, очертания компонентов в ней, и т.д., и т.п. Поэтому я решил упомянуть некоторые архитектурные паттерны, непосредственно касающиеся бизнес-логики. Пусть даже в этой статье придется ограничиться простым списком, рекомендую вам познакомиться с ним и обдумать идеи, связанные с этими паттернами. Вот, пожалуйста:
Крайне маловероятно, что вы окажетесь на проекте тем участником, кто единолично отвечает за процесс проектирования системы. Напротив, скорее всего, вам придется взаимодействовать с коллегами, работающими как в рамках вашей задачи, так и за ее пределами. В данном случае может потребоваться оценивать выбранные технологические решения вместе с коллегами, вычленять бизнес-потребности и понимать, как лучше распараллеливать задачи.
В первую очередь потребуется выработать точное и общепризнанное представление о том, какова та бизнес-цель, которую вы пытаетесь достичь, и с какими подвижными элементами при этом придется иметь дело. Приемы группового моделирования, в частности, штурм событий (event storming) помогают значительно ускорить этот процесс и повышают ваши шансы на успех. Этой работой можно заняться до того или после того, как вы очертите границы ваших сервисов, а затем углублять его по мере созревания продукта. Опираясь на тот уровень согласованности, что будет достигнут здесь, вы также можете сформулировать единый язык для того ограниченного контекста, в котором работаете. Когда потребуется рассказывать об архитектуре вашей системы, для этого вам может пригодиться модель C4, предложенная Саймоном Брауном, особенно когда требуется понять, насколько вам придется углубляться в детали проблемы, визуализируя вещи, которые вы хотите сообщить.
Вероятно, в этой теме найдется и другая зрелая технология, не менее полезная, чем предметно-ориентированное проектирование. Однако, мы так или иначе возвращаемся к пониманию предметной области, поэтому знания и опыт в сфере предметно-ориентированного проектирования должны вам пригодиться.