распределенную конкурентную сущность наших приложений следует явно отразить в модели программирования и использовать в качестве преимущества
Прежде всего мы хотим построить отзывчивую систему. Это означает, что система должна своевременно реагировать на действия пользователя в любых условиях.
Эта книга задумывалась как исчерпывающее руководство по реактивным системам, которое поможет понимать и проектировать их
этой главе (в частности, когда мы затронем устойчивость) будет полезно вспомнить разницу между ошибками и сбоями, определение которой дается в словаре терминов66 манифеста реактивного программирования: «Сбой — это неожиданное событие, которое произошло внутри сервиса и не дает ему нормально работать дальше. В большинстве случаев он делает невозможным получение ответа на текущий и, вероятно, на все последующие клиентские запросы. Это контрастирует с ошибками, представляющими собой ожидаемые условия, предусмотренные в коде (например, ошибка, обнаруженная во время проверки ввода), и передаваемыми клиенту в рамках штатной обработки сообщения. Сбои происходят неожиданно и требуют вмешательства, чтобы система смогла вернуться к прежнему уровню выполнения. Это не означает, что все сбои являются фатальными, но они определенно влекут за собой снижение работоспособности системы. Ошибки представляют собой часть нормального процесса, они обрабатываются без промедления и не влияют на функциональность».
В качестве примеров сбоев можно привести аппаратные неполадки, завершение процесса из-за фатальной нехватки ресурсов и программные дефекты, вызывающие повреждение внутреннего состояния.
Родительский компонент отвечает за функционирование своих потомков, поэтому было бы логично делегировать ему обработку ошибок, с которыми нельзя справиться локально. Такой подход позволяет создавать программное обеспечение, остающееся надежным даже в непредвиденных ситуациях, что является краеугольным камнем обеспечения устойчивости.
Закон Конвея гласит: «Любая организация, проектирующая систему, неизбежно придет к архитектуре, которая является копией структуры взаимодействия внутри этой организации»48. Это говорит о том, что в примере с Gmail у нас получилось бы три модуля, которые отвечают за клиентскую часть, бизнес-логику и базу данных. В рамках этих модулей каждая команда, вероятно, определила бы собственные подмодули для контактов, входа в систему, профиля и почты (рис. 6.3).
Разделение крупной проблемы на множество мелких приводит к дополнительной трате ресурсов на обеспечение сосуществования отдельных решений в рамках конечного продукта. Это превращает изначально ужасающе сложную задачу в целую армию задач поменьше, которые могут сразить вас наповал.
В сервис-ориентированной архитектуре связывание обычно выполняется с помощью фреймворка для внедрения зависимостей, который обозначает точное местоположение каждого компонента. Обычно речь идет о сочетании протокола (адреса, например имени узла и порта) и каких-то подробностей, характерных для этого протокола, например пути. В ходе разрешения зависимостей сначала определяется местоположение ресурса, а затем создается объект, который представляет его в контексте внедрения. Этот процесс выполняется во время запуска, и связи обычно не меняются на протяжении всего жизненного цикла приложения или сервиса.
Сообщение, отправленное удаленно, рискует быть потерянным значительно чаще, чем то, которое передается локально, — например, сетевое оборудование может оказаться неисправным или перегруженным, что приведет к потере пакетов. В связи с этим удаленный обмен сообщениями влечет за собой повышенную вероятность потерь. Протокол TCP смягчает последствия определенного вида ошибок, но он не панацея. Он бессилен, когда, к примеру, соединения разрываются активными сетевыми компонентами.
Превосходство асинхронной передачи сообщений становится еще очевидней в ситуациях, когда получателей несколько. Было бы крайне неэффективно ждать, когда все получатели будут готовы к взаимодействию, или даже просто передавать сообщения по очереди. В реальном мире первый вариант означал бы, что вам нужно организовать целое совещание и уведомить одновременно всех членов команды, второй вариант подразумевает, что вам придется ходить по офису от стола к столу и терпеливо ждать, когда освободится каждый из ваших сотрудников. Вместо этого было бы куда разумней послать асинхронное сообщение — письмо (когда-то бумажное, а теперь, скорее всего, электронное). Поэтому для удобства под обменом сообщениями мы будем понимать асинхронное взаимодействие на основе сообщений между источником и любым количеством потребителей.