среда, 15 января 2014 г.

[prog] Несколько раздраженных комментариев по ходу чтения "The Reactive Manifesto"

Критиковать всегда легче, чем создавать что-то свое. Поэтому я опасаюсь писать исключительно критические посты. Но читая документ под названием "The Reactive Manifesto" эмоции захватывали настолько сильно, что удержаться не смог. Поскольку это очередной призыв вида "Лучше быть здоровым и богатым, чем бедным и больным" или, ближе к программерской теме, пустой выхлоп на тему "Программы должны быть быстрыми, отзывчивыми и надежными", без полезной для разработчиков конкретики. За исключением, разве что, рекламы очередной серебряной пули под названием "event-driven programming", без каких-либо конкретных деталей, понятное дело.

Что дает мне право так жестко говорить на счет изложенных в манифесте вещей? То, что к тому самому event-driven я имею непосредственное отношение уже около двадцати лет. А года с 2001-го только тем и занимался, что разрабатывал ПО, базирующееся на асинхронном обмене сообщениями. Наблюдал за развитием написанного с использованием этого подхода ПО в течении длительного времени, занимался поиском багов, устранением узких мест, масштабированием, добавлением нового функционала, сопровождением и серьезными переделками. Так что прочувствовал всю прелесть этого подхода на себе. И твердо могу сказать: чудес не бывает. Event-driven и asynchronous messaging -- это не пустые слова, в ряде предметных областей успешно работают. Но никакого волшебства нет, есть обычная кропотливая работа. Поэтому, если у кого-то есть надежда, что стоит только взять на вооружение event-driven+async messages, так сразу на него обрушится огромное щасте с большой буквы Щ, тот сильно заблуждается :)

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

Итак, документ декларирует четырех "китов" всей идеи реактивных приложений:


React to events. Приложение должно строится на обмене асинхронными сообщениями и на обработке событий. Мол, если в основе приложения будет лежать неблокирующие друг друга операции, то приложение будет более отзывчивым и будет демонстрировать выполнение своих действий лучше, чем когда те же операции выполняют параллельно работающие процессы/потоки, общающиеся между собой посредством синхронных (т.е. блокирующих) вызовов.

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

Например, при асинхронном взаимодействии очень остро встает вопрос контроля за перегрузкой отдельных частей приложения. Например, компонент A способен обрабатывать входящие запросы за 5 миллисекунд. Тогда как компонент B, получающий сообщения от A, работает в четыре раза медленнее, и обрабатывает сообщения по 20 миллисекунд. Если не вводить каких-то способов синхронизации скоростей работы A и B, то A сможет нагенерировать для B больше работы, чем B будет в состоянии выполнить. Что еще хуже, так это неудержимый рост размеров очередей сообщений, вызванное этим снижение скорости работы приложения или вообще его вылет из-за исчерпания ресурсов.

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


React to load (да еще с пояснением focus on scalability by avoiding contention on shared resources). Ну в прямом смысле "лучше быть богатым, чем бедным". Работа с shared resources возникает не из-за того, что все прям горят желанием делать именно так. Либо из-за того, что по-другому не умеют в принципе. А потому, что такие ресурсы, ну бывает так в жизни, оказываются в ограниченном количестве.

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

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


React to failure. Ну это вообще мой любимый пункт, поскольку кратко он раскрывается следующим образом: build resilient systems with the ability to recover at all levels. Т.е. писать системы нужно так, чтобы они могли восстанавливаться на каком бы логическом уровне не произошла ошибка.

Нет цензурных слов, чтобы комментировать такое. Проблема отказоустойчивости и восстановления работоспособности приложения настолько сложна, что в двух словах о ней не расскажешь. Интересующихся адресую на несколько своих старых заметок: о fail fast, restart quickly; о принципе let-it-crash; о защищенном программировании.

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

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


React to users (с пояснением honor response time guarantees regardless of load). Вновь благие намерения. Поведение системы и нагрузка на систему тесно связаны и сильно зависят от назначения системы. Например, если вы делаете систему он-лайн платежей через банкомат, то время обслуживания транзакции для вас очень важно: вы не можете держать клиента перед банкоматом слишком долго (счет идет на десятки секунд). Для выполнения операции вам придется провзаимодействовать с кучей внешних систем с непредсказуемым временем реакции. И при этом обеспечить надежность проведения финансовых операций. Правда, нагрузка на вашу систему, в приципе, ограничена размером банкоматной сети, хотя и здесь бывают варианты ;)

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

Так что продекларировать желание обеспечить приемлимое время отклика каждому юзеру вне зависимости от нагрузки на систему -- это одно. А вот реализовать это на практике... Конечно, можно очень быстро отвечать части юзеров "Извините, но в данный момент мы не можем обработать ваш запрос". Только не всегда это нужно тем самым юзерам.


В завершении вот чего хочу сказать.

Если вы никогда не слышали про actor model, event-driven programming, asynchronous messaging и пр. вещи, то можно для общего развития этот The Reactive Manifesto и прочитать. Если же слышали и более-менее представляете себе, где это все уместно и чем это чревато, то лучше не читать. Там только лозунги и декларация благих намерений. Ничего полезного вы для себя не найдете.

И еще одна важная вещь. Времена, когда модель агентов и обмен сообщениям были чем-то новым и неизведанным давным давно минули. Так же минули времена, когда удовлетворить желание использовать эти вещи можно было лишь написав собственный фреймворк. Теперь вопрос инструментов не стоит в принципе. Хотите попробовать? К вашим услугам Erlang, Akka для Java/Scala, Cloud Haskell для Haskell-я, SObjectizer для C++ и еще много инструментов. Берете себе подходящий инструмент и вперед набивать шишки и накапливать опыт.

В конце-концов, для ряда задач event-driven -- это именно, что нужно. Просто нужно понимать, что чудес не будет. Будет обычная кропотливая работа, с просчетами, багами, поисками узких мест и утечек, переписыванием и пр. прелестями разработки. Серебряной пули, чтобы вам кто не говорил, нет.

Комментариев нет: