Alek�ei Matiu�hkin

сделано с умом



Горизонтальное масштабирование

Tuesday, 26 Dec 2023 Tags: 2023tech

четвертая часть «четырех главных навыков разработчика»

умение сразу делать горизонтально масштабируемое решение, не добавляя специального кода для этого в первой версии


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

Обслуживание десяти миллиардов клиентов еще не означает, что твой сервис масштабируем. Например, автобусы возят людей по Невскому, и автобусы возят людей по Садовому кольцу; любой человек, окончивший три класса церковно-логистической школы скажет вам, что говорить о «налаженном сообщении между Ленинградом и Москвой» на основе этого непреложного факта рановато.

Даже в пределах одного города — ситуация может запросто выйти из-под контроля. Масштабирование автобусного парка по мере разрастания площади мегаполиса сопряжена не с закупкой и выпуском на улицы новых автобусов. Я жил в Берлине двадцать лет назад, и нарадоваться не мог на то, как там организован транспорт. Судя по сегодняшним отзывам пассажиров, с разрастанием города вширь — BVG не справилось.

Вдумчивому читателю предлагаю на этом месте остановиться и подумать: а что же пошло не так с масштабированием сервиса? Для поверхностных ебланов, не желающих вдумываться в прочитанное, сообщаю ответ: логистика.

Выпустить на улицы многомиллионного города дополнительную порцию автобусов — много ума не надо. Но каждому конкретному пассажиру нужен всего один автобус: который подойдет сразу после того, как он вышел из предыдущего. Не через час, не минуту тому назад, а в пределах трех-пяти минут пересадочного времени. Пока автобусное расписание построено с учетом минимизированного (и гарантированного) времени пересадок — мы можем говорить о масштабировании. Если автобус А вывалил пассажиров на конечной спустя тридцать секунд после того, как от нее отошел стыковочный автобус Б — масштабирование провалилось.

Ладно, хрен с ним, с Берлином, там и с хорошим общественным транспортом жить было невозможно. Вернемся к Тоби.

Каждый пользователь Шопифая нуждается в одном постоянном соединении (вебсокете) с одним сервером. Добавление новых пользователей — это просто добавление серверов в стойку. Если там и есть какие-то проблемы с масштабированием — они все сгрудились в районе базы данных, а не самих рельсов. Рельсам вообще все равно, сколько всего серверов обслуживает пользователей: каждый сервер должен справиться только со своей нагрузкой.

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

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

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

Упомяну еще бегло пример из личной практики, и перейду в сути. Мы обрабатываем входящий поток курсов валют, производя с ними некоторые математические манипуляции, и выплевывая причудливый результат. Двести с лишним валют, то есть сорок тысяч пар, значения по каждой приходят в среднем ну пусть раз в секунду (на самом деле чаще). Математические манипуляции могут вовлекать любое количество валют. Все это происходит в реальном времени. Из-за требования доступности значений в любой момент времени, мы не можем просто разбить пары по партициям и разделить потоки на несколько серверов. А один не справляется физически. То есть, каждая нода в кластере должна уметь обмениваться информацией с любой другой нодой, причем информацией не статической, редис не подойдет. Вот тут архитектура нуждается в способности горизонтально масштабироваться.


Что-то предисловие затянулось. Надеюсь, что я хотя бы немного прояснил терминологию. Так вот, если в процессе обсуждения архитектуры вы пришли у выводу, что в проекте потребуется настоящее горизонтальное масштабирование — вам не обойтись без конечных автоматов (так-то лучше вообще любую бизнес-логику строить именно на конечных автоматах, но в автономной системе можно подкостылить и без них, а вот в кластере — уже никак). Я крайне редко рекомендую справочную литературу, но Introduction to the Theory of Computation Майкла Сипсера — проглядеть очень полезно. Конечный автомат — при все кажущейся простоте — штука настолько могущественная, что аж диву даешься.

К сожалению, многие полагают, что FSM — это просто набор состояний; это крайне опасное заблуждение, полностью нивелирующее любую доказательную математику, на которой зиждется мощь конечных автоматов.

Так или иначе, если вы хотите оказаться готовым отмасштабироваться в горизонталь — стройте критические процессы на конечных автоматах и полностью асинхронно. Если подсистема A должна взаимодействовать с подсистемой Б — забудьте про прямые вызовы. Выражаясь на языке HTTP — 201 хорошо, 200 ужасно. Никогда, ни при каких обстоятельствах, вы не сможете потом превратить последовательность вызовов запрос→ответ в запрос→подтверждение получения→ожидание ответа (без разрушительной силы рефакторинга).

Зато асинхронные взаимодействия поверх FSM — сделают в дальнейшем масштабирование безболезненным, ведь в такой парадигме не имеет никакого значения, на какой ноде выполнится код, отвечающий на запрос.

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


И, напоследок, идеальный лакмус для определения, распределена ли ваша система, или это просто несколько серверов по углам. Если вам приходилось решать, какой буквой из набора «C», «A», «P» пожертвовать — скорее всего у вас настоящий масштабируемый кластер. Если нет — забудьте про горизонтальное масштабирование, просто добавляйте новые мощности по мере расцветания бизнеса.

Ну и, пользуясь случаем, не могу не порекомендовать свою библиотеку Finitomata, которую я прототипировал на Idris и которая спроектирована полностью асинхронной (нет возможности узнать из ответа, успешно ли завершился переход из одного состояния в другое) — она доказательно не дает возможности программисту нарушить ни единый закон управления конечными автоматами.


  ¦