Книги
Джейми Аллен,Роланд Кун,Брайан Ханафи

Реактивные шаблоны проектирования

    Timur Ahmetovцитирует3 года назад
    этой главе (в частности, когда мы затронем устойчивость) будет полезно вспомнить разницу между ошибками и сбоями, определение которой дается в словаре терминов66 манифеста реактивного программирования: «Сбой — это неожиданное событие, которое произошло внутри сервиса и не дает ему нормально работать дальше. В большинстве случаев он делает невозможным получение ответа на текущий и, вероятно, на все последу­ющие клиентские запросы. Это контрастирует с ошибками, представляющими собой ожидаемые условия, предусмотренные в коде (например, ошибка, обнаруженная во время проверки ввода), и передаваемыми клиенту в рамках штатной обработки сообщения. Сбои происходят неожиданно и требуют вмешательства, чтобы система смогла вернуться к прежнему уровню выполнения. Это не означает, что все сбои являются фатальными, но они определенно влекут за собой снижение работоспособности системы. Ошибки представляют собой часть нормального процесса, они обрабатываются без промедления и не влияют на функциональность».
    В качестве примеров сбоев можно привести аппаратные неполадки, завершение процесса из-за фатальной нехватки ресурсов и программные дефекты, вызывающие повреждение внутреннего состояния.
    Timur Ahmetovцитирует3 года назад
    Родительский компонент отвечает за функционирование своих потомков, поэтому было бы логично делегировать ему обработку ошибок, с которыми нельзя справиться локально. Такой подход позволяет создавать программное обеспечение, остающееся надежным даже в непредвиденных ситуациях, что является краеугольным камнем обеспечения устойчивости.
    Timur Ahmetovцитирует3 года назад
    Закон Конвея гласит: «Любая организация, проектирующая систему, неизбежно придет к архитектуре, которая является копией структуры взаимодействия внутри этой организации»48. Это говорит о том, что в примере с Gmail у нас получилось бы три модуля, которые отвечают за клиентскую часть, бизнес-логику и базу данных. В рамках этих модулей каждая команда, вероятно, определила бы собственные подмодули для контактов, входа в систему, профиля и почты (рис. 6.3).
    Timur Ahmetovцитирует3 года назад
    Разделение крупной проблемы на множество мелких приводит к дополнительной трате ресурсов на обеспечение сосуществования отдельных решений в рамках конечного продукта. Это превращает изначально ужасающе сложную задачу в целую армию задач поменьше, которые могут сразить вас наповал.
    Timur Ahmetovцитирует3 года назад
    В сервис-ориентированной архитектуре связывание обычно выполняется с помощью фреймворка для внедрения зависимостей, который обозначает точное местоположение каждого компонента. Обычно речь идет о сочетании протокола (адреса, например имени узла и порта) и каких-то подробностей, характерных для этого протокола, например пути. В ходе разрешения зависимостей сначала определяется местоположение ресурса, а затем создается объект, который представляет его в контексте внедрения. Этот процесс выполняется во время запуска, и связи обычно не меняются на протяжении всего жизненного цикла приложения или сервиса.
    Timur Ahmetovцитирует3 года назад
    Сообщение, отправленное удаленно, рискует быть потерянным значительно чаще, чем то, которое передается локально, — например, сетевое оборудование может оказаться неисправным или перегруженным, что приведет к потере пакетов. В связи с этим удаленный обмен сообщениями влечет за собой повышенную вероятность потерь. Протокол TCP смягчает последствия определенного вида ошибок, но он не панацея. Он бессилен, когда, к примеру, соединения разрываются активными сетевыми компонентами.
    Timur Ahmetovцитирует3 года назад
    Превосходство асинхронной передачи сообщений становится еще очевидней в ситуациях, когда получателей несколько. Было бы крайне неэффективно ждать, когда все получатели будут готовы к взаимодействию, или даже просто передавать сообщения по очереди. В реальном мире первый вариант означал бы, что вам нужно организовать целое совещание и уведомить одновременно всех членов команды, второй вариант подразумевает, что вам придется ходить по офису от стола к столу и терпеливо ждать, когда освободится каждый из ваших сотрудников. Вместо этого было бы куда разумней послать асинхронное сообщение — письмо (когда-то бумажное, а теперь, скорее всего, электронное). Поэтому для удобства под обменом сообщениями мы будем понимать асинхронное взаимодействие на основе сообщений между источником и любым количеством потребителей.
    Timur Ahmetovцитирует3 года назад
    Каждый актор содержит только один поток выполнения, и к нему нельзя обратиться непосредственно из другого потока, поэтому в рамках актора отсутствует параллелизм (разве что вы добавите его поддержку каким-то другим способом). Таким образом, акторы могут инкапсулировать изменяемое состояние без необходимости ограничивать доступ к переменным с помощью блокировок.
    Timur Ahmetovцитирует3 года назад
    Впервые модель акторов была предложена Карлом Хьюиттом в 1973 году. Это концепция конкурентных вычислений, в которой все взаимодействие происходит между сущностями под названием «акторы» и реализуется путем передачи сообщений отправителем и помещения их в очередь получателем41. Язык программирования Erlang, который одним из первых стал поддерживать разработку реактивных приложений, использует акторы в качестве основной архитектурной конструкции. Благодаря успеху фреймворка Akka на платформе JVM акторы в последнее время стали популярными.
    Timur Ahmetovцитирует3 года назад
    Future — это ссылка на значение или код ошибки, которая может стать доступной (только для чтения) в какой-то момент времени, Promise — это соответствующий дескриптор с возможностью одиночной записи, который обеспечивает доступ к значению.
    Timur Ahmetovцитирует3 года назад
    Циклы обработки событий чаще всего реализуются с помощью функций обратного вызова. В этом не было бы ничего плохого, если бы одновременно можно было ссылаться только на один поток, но по мере наращивания функциональности приложения такой вариант обычно исключается. Термины «ад обратных вызовов» (callback hell) и «пирамида судьбы» (pyramid of doom) были придуманы, чтобы описать запутанный спагетти-код, который часто получается при использовании инструментов наподобие Node.js. Более того, циклы обработки событий, основанные на однопоточном процессе, подходят только для ситуаций, связанных с обработкой ввода/вывода. При работе с ресурсоемкими операциями все преимущества данного подхода теряются.
    Timur Ahmetovцитирует3 года назад
    Отзывчивостью в реактивных приложениях является способность быстро реагировать на запросы пользователя с учетом сбоев, которые могут произойти где угодно — как внутри, так и вовне. Компромиссы, вызванные реактивным подходом, определяются аксиомой, согласно которой вы можете выбрать лишь две из трех характеристик таких, как:
    • высокая пропускная способность;
    • малое время ожидания, плавная работа;
    • малое потребление ресурсов.
    Timur Ahmetovцитирует3 года назад
    Используя неизменяемость в своем приложении, вы можете свести к минимуму объем кода, в котором допустимы изменения. Поэтому вероятность соперничества разных потоков за одновременный доступ к одному и тому же ресурсу (что заставляет некоторые из них ждать своей очереди) ограничена небольшой областью.
    Timur Ahmetovцитирует3 года назад
    Следующий шаг назывался сервис-ориентированной архитектурой (service-oriented architecture, SOA).
    Timur Ahmetovцитирует3 года назад
    Существует лишь один универсальный способ защитить систему в целом, когда выходят из строя отдельные ее части: сделать ее распределенной и разделенной. Первое можно перефразировать так: «не складывайте все яйца в одну корзину», а второе: «держите свои корзины подальше друг от друга». Когда дело доходит до обработки сбоя, важно делегировать обязанности, чтобы сломавшийся компонент не был ответственен за собственное восстановление.
    Timur Ahmetovцитирует3 года назад
    Но устойчивость в целом касается не только отказов или сбоев: устойчивая система восстанавливает свою оригинальную форму и набор возможностей. В качестве примера возьмем спутник, выведенный на орбиту. Чтобы снизить риск провала миссии, каждую критически важную функцию, аппаратную или программную, дублируют. На случай, если какой-то компонент выйдет из строя, предусмотрена процедура переключения на запасной вариант. Такая реализация отказоустойчивости позволит спутнику продолжать работу, но с этого момента затронутый сбоем компонент уже не сможет справиться с дополнительными сбоями, так как у него была всего одна резервная копия. Это означает, что системы спутника способны справляться с неполадками, но их нельзя назвать устойчивыми.
    Timur Ahmetovцитирует3 года назад
    Проще говоря, это означает, что увеличение вычислительных ресурсов в n раз приводит к n-кратному повышению производительности. Если спроектировать систему, состоящую из полностью изолированных компонентов, которые выполняются независимо друг от друга, это будет единственным теоретическим ограничением (при условии, что задачу удастся разбить на n частей). На практике вам придется обмениваться запросами и ответами, что тоже потребует некой разновидности синхронизации, но затраты на нее будут крайне низкими. Даже недорогое аппаратное обеспечение позволит передавать между ядрами процессора несколько сотен миллионов сообщений в секунду.
    Timur Ahmetovцитирует3 года назад
    Универсальный закон масштабируемости предусматривает еще одно свойство, определяющее, сколько времени уходит на обеспечение согласованности данных в системе. Этот фактор называется когерентностью и объединяет в себе все задержки, связанные с координацией потоков, призванной гарантировать согласованный доступ к разделяемым структурам данных.
    Timur Ahmetovцитирует3 года назад
    Закон Амдала определяет максимальный прирост скорости, которого можно достичь, используя дополнительные потоки
    Здесь N — это количество потоков, — сериализованная часть программы, а T(N) — время, необходимое алгоритму на выполнение N потоков.
    Timur Ahmetovцитирует3 года назад
    Предположим, что любой запрос к БД занимает одно и то же время W, и пока забудем про кэш (вернемся к нему в подразделе «Закон Амдала» раздела 2.3). Мы хотим узнать, сколько соединений с базой данных L потребуется при заданной нагрузке . Ответ на этот вопрос дает формула, называемая теоремой Литтла:
    L = х W.
    Теорема Литтла справедлива для долгосрочных средних значений трех величин, не зависящих от момента начала запросов или порядка их обработки. Если базе данных требуется для ответа в среднем 30 мс и система принимает 500 з./с (запросов в секунду), вы можете применить следующую формулу:
    L = 500 з./с х 0,03 с/з. = 15.
fb2epub
Перетащите файлы сюда, не более 5 за один раз