Pull to refresh

Объединяем акторов и SEDA-подход: зачем и как?

Reading time 16 min
Views 4.6K

В одной из статей про шишки, которые довелось набить за 15 лет использования акторов в C++, речь зашла о том, что большое количество акторов — это, зачастую, сама по себе проблема, а отнюдь не решение. И что использование идей из SEDA-подхода может существенно упростить жизнь при разработке приложений на базе модели акторов. Однако, как показали затем вопросы в комментариях к предыдущим статьям, совмещение SEDA-подхода и модели акторов отнюдь не очевидно, поэтому есть смысл копнуть данную тему чуть глубже.


Модель акторов и ее достоинства


Пара слов о модели акторов


В модели акторов прикладная работа выполняется посредством специальных вычислительных сущностей, называемых акторами, с использованием трех основных принципов:


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

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


Модель акторов не определяет, что из себя должны представлять акторы. Поэтому реализации модели акторов могут выглядеть сильно по-разному. Так, одной из самых известных реализаций модели акторов считается язык программирования Erlang. Там актором является легковесный процесс, работающий в рамках Erlang VM. В еще одной из самых известных реализаций модели акторов, фреймворке Akka для JVM, акторы представляются в виде объектов, методы которых автоматически вызываются фреймворком при получении сообщения для актора. Тогда как в другой реализации акторов для JVM, Quasar, актор — это сопрограмма. В мире C++ такие фреймворки, как QP/C++ и SObjectizer, используют представление актора как объекта, являющегося конечным автоматом. В фреймворке CAF актор может быть как объектом, так и функцией. А в фреймворке Just::Thread Pro: Actors Edition каждый актор — это отдельная нить ОС.


Соответственно, отличаются и принципы работы разных реализаций: где-то фреймворк вызывает методы-коллбэки у объектов-акторов, где-то сами акторы вынуждены дергать фреймворк для получения очередного входящего сообщения. Но общая схема работы в любом случае сохраняется: если для актора нет входящего сообщения, то он спит и ничего не делает. Как только сообщение появляется, то актор просыпается, обрабатывает входящее сообщение и засыпает в ожидании следующего входящего сообщения.


В чем же удобство акторов?


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


Во-первых, это то, что каждый актор обладает собственным состоянием и общается с внешним миром только посредством асинхронных сообщений. Т.е. воплощение архитектуры share nothing в чистом виде. Важность этого сложно переоценить как при многопоточном программировании, так и при построении распределенных приложений.


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


Модель акторов, в принципе, хорошо дружит с распределенностью, поскольку при асинхронном обмене сообщениями не суть важно, расположен ли получатель внутри того же самого процесса или же он работает на удаленном узле. Однако, вопрос распределенности гораздо более сложен, особенно если речь идет об интенсивном обмене сообщениями между акторами, так что здесь все не так однозначно. Но, если речь не идет о больших нагрузках, то построить распределенное приложение на базе модели акторов не сложно, особенно если фреймворк/среда предоставляет такую возможность из коробки.


Во-вторых, каждый актор может представлять из себя некоторую автономную сущность, работающую в соответствии с собственной логикой. Например, в реляционной СУБД можно создавать акторов для выполнения SQL-запросов. Каждый актор может выполнять запрос от начала до конца: разбирать запрос, проверять его корректность, строить план выполнения, запрашивать данные из системы хранения, выделять удовлетворяющие запросу данные, отсылать данные отправителю запроса. Всю эту логику бывает удобно реализовать в рамках одной сущности, в виде актора. После чего приложение (например, сервер РСУБД) будет создавать N акторов, каждый из которых независимо от других обслуживает различные запросы.



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


Подход SEDA и его достоинства


SEDA-подход в нескольких словах


Суть SEDA подхода состоит в том, что каждая конкретная прикладная операция разбивается на отдельные стадии и под каждую стадию выделяется отдельная вычислительная сущность. Так, обслуживание SQL-запроса в РСУБД может быть разбито на следующие стадии: разбор SQL-запроса, валидация параметров запроса, построение плана запроса, поднятие данных из хранилища, фильтрация удовлетворяющих запросу данных, отсылка результатов отправителю запроса.


Взаимодействие между стадиями осуществляется посредством отсылки асинхронных сообщений. Так, в случае SEDA-подхода отдельная сущность-стадия выполняет разбор SQL-выражения. Если разбор завершился успешно, то разобранное представление SQL-запроса отсылается следующей сущности-стадии. Следующая сущность выполняет валидацию параметров уже разобранного запроса. Если параметры валидны, то запрос отсылается следующей сущности-стадии, которая займется подготовкой плана выполнения запроса. И т.д.



Фишка SEDA-подхода в том, что очереди сообщений между сущностями-стадиями могут использоваться для контроля за нагрузкой на приложение. Допустим, очередь между стадиями «разбор» и «валидация» может иметь фиксированный размер в 20 элементов. Это означает, что стадия «разбор» не сможет поставить в очередь очередное сообщение, если очередь к стадии «валидация» полностью заполнена.


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


  • можно приостановить текущую сущность-стадию до тех пор, пока не освободится место в очереди;
  • можно переслать сообщение другой сущности-стадии, которая выполнит обработку иначе (например, в случае невозможности обслужить запрос из-за перегрузки можно сформировать специальный ответ «retry after»);
  • можно попробовать изъять из очереди какое-то старое сообщение (например, в ряде случаев можно выбрасывать сообщения, которые ждут своей очереди слишком долго, т.к. с большой долей вероятности результат их обработки уже не будет никому нужен).

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


Достоинства SEDA-подхода


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


Однако, наиболее важное достоинство SEDA-подхода — это врожденная защита от перегрузки. В модели акторов перегрузка акторов является ахилесовой пятой. А вот в SEDA-подходе разработчик сразу должен позаботиться о защите своих сущностей-стадий от перегрузки и выбрать один из штатных механизмов, либо же реализовать и задействовать какой-то собственный. Что самым положительным способом сказывается на устойчивости и отзывчивости приложения под высокой нагрузкой.


Объединение модели акторов и подхода SEDA


Зачем объединять модель акторов и SEDA-подход?


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


Акторы не защищены от перегрузки


Одна из отличительных черт модели акторов — это асинхронный обмен сообщениями. Именно благодаря этому при использовании акторов можно не беспокоится о дедлоках. Но у этой положительной черты есть и своя цена: очереди сообщений для акторов не ограничены в размерах. Это означает, что если актор A отсылает сообщения актору B быстрее, чем актор B их обрабатывает, то размер очереди актора B будет постоянно расти. Еще хуже, когда актору B сообщения летят не только от актора A, но и от акторов C, D, E и далее по списку.


Подробнее эта тема рассматривалась в одной из предыдущих статей.


Примечание. Вообще говоря, ограниченность или неограниченность входящих очередей для акторов — это особенность конкретного фреймворка и среды исполнения. В SObjectizer, например, можно накладывать ограничения на количество ожидающих обработки сообщений. Но, в большинстве случаев, по умолчанию очереди для акторов не имеют фиксированного размера.


Множество акторов сложнее координировать


Когда в приложении существует множество акторов, их активность может распределиться во времени таким образом, что приложение перестанет подавать признаки жизни. Например, выше мы говорили о подходе, когда в сервере СУБД акторы используются для выполнения SQL-запросов, при этом каждый запрос полностью обрабатывается одним актором. Вполне может быть так, что операция валидации параметров запроса сильно нагружает CPU, тогда как операция поднятия данных из хранилища не грузит CPU, но зато активно использует I/O.


Если разместить всех подобных акторов на одном пуле рабочих нитей, то рано или поздно возникнет ситуация, когда почти все акторы пытаются выполнить валидацию параметров SQL-запросов. Тем самым нагружая CPU «под плашку». А потом они все пытаются обратиться в хранилище за данными и мы упираемся в возможности I/O. По хорошему, нам следовало бы делать так, чтобы только часть акторов могла грузить CPU, в то время, как другая часть задействует I/O. Но такая координация требует дополнительной работы и логика поведения акторов становится более сложной, чем нам изначально хотелось.


Существуют естественные «бутылочные горлышки»


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


Например, акторам может быть необходимо использовать hardware security module (HSM) для выполнения криптографических операций. HSM один. Интерфейс к нему представлен, скорее всего, какой-то сторонней библиотекой, подразумевающей, что вся работа с HSM (инициализация, использование, деинициализация) будет выполняться синхронно.



Другой характерный пример: акторам нужно работать с БД, а количество параллельных подключений к БД сильно ограничено. Скажем, у нас всего 100 параллельных подключений к БД, а акторов — 10000 штук. И им всем нужно работать с БД.


В таких случаях приходится как-то выкручиваться. Скажем, мы можем ввести актора-HSM, который отвечает за работу с HSM. И все наши прикладные акторы должны будут общаться с ним посредством асинхронных сообщений, что не так удобно, как хотелось бы. Или мы можем ввести какой-то механизм токенов: прикладной актор должен получить токен, который дает право работать с HSM напрямую. После того, как актор завершил свои действия с HSM, он должен вернуть токен для того, чтобы токен мог попасть к другому прикладному актору.


Все эти частные случаи не являются сколько-нибудь серьезной проблемой. Но они оказываются теми самыми «оврагами», про которые забыли «на бумаге». А их обход будет требовать дополнительных усилий и усложнения реализации, изначально казавшейся простой и очевидной.


Что дает объединение модели акторов и SEDA-подхода?


Мы можем использовать идеи из SEDA при разработке приложения на базе модели акторов. При этом у нас сущности-стадии будут всего лишь акторами. Получается, что при декомпозиции прикладной задачи мы отталкиваемся от SEDA-подхода, но выделенные в результате декомпозиции стадии реализуем в виде акторов.


Возьмем еще раз пример с РСУБД и обработкой SQL-запросов посредством акторов. В сервере РСУБД у нас могут быть акторы-стадии: разбора SQL-запроса, валидации параметров, построения плана, подъема данных из хранилища, выборки удовлетворяющих условиям запроса данных, отсылки результатов. Каждый из этих акторов будет работать по обычным для акторов правилам: пока нет входящих сообщений, акторы-стадии спят. Когда входящие сообщения появляются, актор-стадия просыпается и выполняет их обработку.


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


Актор-стадия должен выполнять действие, которое может относиться к разным и независимым прикладным операциям. Например, актор-стадия для построения плана выполнения SQL-запроса должен уметь строить план и для insert-запроса, который пришел от клиента Alice, и для update-запроса, который пришел от клиента Bob, и для select-запроса, который пришел от клиента Eve.


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


В пределе от актора-стадии может потребоваться умение группировать ожидающие сообщения так, чтобы их обработка была наиболее эффективной. Например, для актора-стадии, отвечающего в сервере РСУБД за планы запросов, построение планов для insert-запросов может быть намного дешевле, чем построение планов для select-запросов. Поэтому такой актор может сперва обработать все insert-запросы, а затем перейти к обработке select-запросов. Но это в пределе, когда приложение сталкивается с очень высокими нагрузками и приходится оптимизирования все и вся.


Бонус №1: уменьшение общего количества акторов


Это может звучать контринтуитивно, ведь одним из главных аргументов в свою пользу практически каждая реализация модели акторов приводит возможность создавать сотни тысяч, миллионы, а то и десятки миллионов акторов. Казалось бы, миллион акторов в приложении — это увлекательно, срывает шаблоны и открывает новые горизонты… Однако, это не всегда так и мы на своем опыте убедились, что временами чем меньше акторов, тем лучше.


Небольшое количество акторов проще контролировать. Само приложение, в котором работает сотня акторов, гораздо проще мониторить. Экспортировать основные жизненные показатели сотни «тяжелых» акторов в какую-то систему мониторинга, вроде Zabbix-а, и отслеживать их затем через всевозможные мониторинговые консоли и/или системы уведомлений, вполне возможно. А вот проделать то же самое с основными показателями миллионов «легких» акторов уже затруднительно.


Работу небольшого количества акторов проще координировать. Если мы знаем, что акторы A и B являются CPU-bound, то мы можем выделить каждому из них по отдельному потоку ОС и даже можем привязать каждый из этих потоков к своему ядру. Тогда как для акторов C, D и E, являющихся I/O-bound и выполняющих асинхронный ввод-вывод, мы можем выделить одну общую рабочую нить. Ну и чем меньше акторов, тем проще им договориться о том, кто, когда, как и что из ресурсов будет потреблять.



Примечание. Не всегда сокращение количества акторов — это хорошо. Могут быть задачи, в которых сотни миллионов одновременно существующих акторов — это более чем нормально. Например, фреймворк Orleans для .NET использовался для многопользовательской игры Halo (Halo 4 и Halo 5), где каждый пользователь представлялся отдельным актором, что выглядит как вполне разумный подход к решению подобной задачи. Тем не менее, в каких-то случаях чем меньше одновременно живущих акторов, тем проще.


Бонус №2: естественным образом выражаются «бутылочные горлышки»


Когда мы используем акторы-стадии, то быстро обнаруживаем, что существующие «бутылочные горлышки» (т.е. ресурсы, которые невозможно предоставить всем одновременно живущим акторам) простым и естественным образом выражаются в виде акторов-стадий. И работа с такими «бутылочными горлышками» перестает быть особенной.


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


А вот если стадия выполнения криптографических операций (например, шифрование и подпись исходящего документа) представлена соответствующим актором-стадией, то:


  • этот актор-стадия будет иметь эксклюзивный доступ к HSM, что достаточно удобно в реализации и последующем сопровождении;
  • предшествующие в цепочке акторы-стадии вообще не заботятся об особенностях работы с HSM. Они просто подготавливают исходящий документ, который должен быть зашифрован и подписан, после чего передают документ актору-стадии, который владеет HSM-ом, и переходят к подготовке следующего документа.

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


Бонус №3: возможность использования bulk-операций


Представьте себе, что вы на акторах реализуете MQ-шный брокер. И каждый актор отвечает за обслуживание своей темы (один topic == один актор). И вам нужно обеспечить персистентность для публикуемых сообщений, посредством записи новых сообщений в БД. Т.е., получил актор-topic новую команду publish — должен сперва сохранить сообщение в БД, а уже затем может отослать publish_ack с подтверждением.


Если каждый актор-topic будет самостоятельно выполнять операции с БД, то мы можем столкнуться с ситуацией, когда над БД выполняется множество мелких транзакций (множество одиночных insert-ов в одни и те же таблицы, множество одиночных delete из тех же таблиц). Что не есть хорошо.


А вот если у нас есть отдельный актор для выполнения операции publish, то у него появляется возможность выполнить сразу несколько insert-ов в таблицу БД посредством bulk-операции. С точки зрения увеличения пропускной способности (throughput) MQ-шного брокера это гораздо выгоднее множества одиночных insert-ов.


Плата за бонусы: контроля за перегрузкой из коробки нет


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


Поэтому нужно самому уделять внимание защите акторов-стадий от перегрузки (overload control). И, возможно, придется озадачиться реализацией какой-то обратной связи (back pressure).


В принципе, здесь нет ничего сложного. Очень хорошо в деле защиты от перегрузки себя зарекомендовали пары из актора-коллектора и актора-исполнителя, которые работают на разных контекстах (разных рабочих нитях).


Актор-коллектор получает сообщения и накапливает их в своей внутренней очереди фиксированного размера. Если эта очередь переполняется, то актор-коллектор может предпринять какое-то подходящее для прикладной логики приложения действие: выбросить какое-то из старых сообщений, переслать сообщение другому актору или же отослать отрицательный ответ.


Актор-коллектор весьма полезен в ситуации, когда сообщения могут дублироваться. Скажем, актор A отсылает сообщение Req и ждет в ответ Resp в течении 5 секунд, если же Resp не получен, то A перепосылает Req. Когда приложение работает под нагрузкой и обработка Req начинает подтормаживать, то A может отослать несколько Req, прежде чем до него дойдет первый Resp. Если все Req проходят через актор-коллектор, то актор-коллектор может быть способен выявить дубликаты Req и устранить их.


Актор-исполнитель забирает у актора-коллектора накопленные сообщения пачками. После того, как актор-исполнитель обработает очередную пачку, он обращается к актору-коллектору за следующей пачкой и т.д.



Эта схема из двух акторов проста и надежна, кроме того, ее достаточно легко мониторить (можно вынести такие параметры, как количество накопленных сообщений и время обработки очередной пачки сообщений, для контроля инструментами вроде Zabbix). Но ее нужно реализовать вручную. В том же C++ можно использовать шаблонные классы для уменьшения копипасты при разработки таких акторов-коллекторов. Тем не менее, все это должен сделать разработчик приложения, а это расходы и на стадии разработки, и на стадии сопровождения.


Если SEDA-подход так хорош, то почему бы не использовать только его и вообще обойтись без акторов?


На самом деле это вопрос из категории «если подход подставьте-здесь-любое-название так хорош, то почему бы не пользоваться только им?»


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


Именно так дело обстоит с моделью акторов: где-то достаточно только ее. Где-то нам приходится бороться с ее недостатками. Где-то мы ее вообще не используем.


Аналогично и с SEDA-подходом. Где-то этот подход может использоваться для разработки всего приложения. Но, скорее всего, одного SEDA-подхода вам окажется недостаточно. Какие-то части приложения просто не будут однозначно и легко отображаться на стадийность прикладных операций. И нам придется дополнять SEDA-подход еще чем-то. Теми же акторами, например.


Кроме того, на практике у вас встанет вопрос: а что из себя представляют сущности-стадии на уровне реализации? Будет ли каждая стадия потоком ОС? Или это будет объект с методами-коллбэками? Будет ли логика работы стадии описываться конечным автоматом? Должна ли стадия выполнять какие-то действия, пока для нее нет новых заявок на обработку, или же стадия должна спать в ожидании новых заявок? В итоге может оказаться, что в коде сущность-стадия будет тем же самым актором, только в профиль. Ну а раз так, то модель акторов и готовые ее реализации просто-напросто напрашиваются в качестве готовой базы для реализации сущностей-стадий.


Обезличенный пример из собственного опыта


Мы сами не сразу пришли к пониманию выгод комбинирования модели акторов и SEDA-подхода и успели набить себе некоторое количество шишек, часть из которых описывалась в двух написанных ранее статьях (№1 и №2). Но даже когда мы начали комбинировать эти два подхода, то так же не сразу определили для себя модель, которая удовлетворяет именно нашим условиям.


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


Приложение обрабатывало несколько потоков сообщений. В каждом потоке были сообщения одного типа. Обработка каждого потока строилась по-своему, хотя какие-то общие черты и похожести были.


Для обработки каждого потока было несколько стадий. Каждую стадию обслуживала пара акторов (коллектор и исполнитель). Актор-исполнитель работал на потактовой основе с типичным тактом в 250ms (хотя шаг таймера настраивался и можно было сделать такт в 50ms или в 2s). На очередном такте актор-исполнитель просыпался, забирал у актора-коллектора все накопленные сообщения, после чего фиксировал новые сообщения в БД, выбирал сообщения, которые были готовы к отправки на следующую стадию и отсылал их, обрабатывал накопленные ответы, фиксировал их в БД (доставкой зафиксированных ответов занимались другие акторы). После чего проверял, осталось ли еще время на текущем такте. Например, вся работа могла занять 200ms из 250ms выделенных под такт. В этом случае актор-исполнитель засыпал на оставшиеся 50ms и просыпался лишь на следующем такте. Если же операции на текущем такте заняли слишком много времени, скажем, потребовалось 300ms вместо 250ms, то следующий такт начинается сразу же.


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


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


Заключение


В этой статье мы попробовали немного рассказать о своем опыте комбинирования модели акторов и SEDA-подхода. Надеемся, что наш рассказ окажется для кого-то интересным и полезным. Однако, мы понимаем, что модель акторов сейчас используется много где, кроме того, находящиеся в эксплуатации реализации модели акторов очень сильно отличаются друг от друга (даже если они сделаны для одной платформы, как, например, Akka и Quasar для JVM). Поэтому имеет смысл сделать поправку на ветер и радиус кривизны наших рук при попытке переиспользовать наш опыт у себя.

Tags:
Hubs:
+9
Comments 38
Comments Comments 38

Articles