По akka мало материалов на Хабре. Решил перевести некоторые антипаттерны, описанные Мануэлем в своем блоге. Они действительно могут быть неочевидны людям впервые столкнувшимся с фреймворком.
Мне пришло в голову, что я еще не писал об этом очень частом анти-паттерне. Его часто можно найти в коде разработчиков, которые только начинают работать с моделью акторов.
Есть два пути получить слишком много акторов:
— разработав систему со слишком большим количеством различных типов акторов, многие из которых не нужны
— создав очень большое количество акторов в рантайме, когда это не нужно и неэффективно
Давайте посмотрим на эти варианты в деталях.
Слишком много типов акторов
Общая идея примерно такая: «у нас есть акторы, поэтому все должно быть актором».
Модель акторов упрощает написание асинхронных приложений. Она делает это, обеспечивая иллюзию синхронного выполнения кода внутри актора – нет необходимости беспокоиться о параллельном доступе к состоянию одного актора, потому что только актор может получить доступ к своему состоянию, и сообщения обрабатываются по одному за раз, по очереди.
Но на самом деле, не все нужно делать асинхронно. Вызовы методов, которые связаны исключительно с ЦП (и не «блокируются» в том смысле, что они не полностью перегружают ЦП, например, вычисление значения Пи), не должны выполняться асинхронно.
Я довольно часто вижу код с большим количеством различных акторов, взаимодействующими друг с другом и не делающими ничего, что имеет большое преимущество в асинхронном или одновременном выполнении. В этих проектах одно и то же состояние должно храниться каждым из этих акторов или передано им в каждом сообщении.
Этот подход имеет два недостатка:
— Вы ничего не получаете с точки зрения производительности. Даже наоборот, есть накладные расходы, связанные с созданием сообщений и их передачей.
— С каждым типом актора и связанными с ним сообщениями, система становится более сложной для понимания и обслуживания.
Поэтому при проектировании систем акторов надо думать о том, что на самом деле должно быть асинхронным, в основном это:
— вызовов внешних систем (за пределами вашей jvm)
— вызовы блокирующих операций (устаревшие API, тяжелые вычисления, …)
Слишком много акторов в рантайме
Общая идея примерно такая: ”чем больше у нас акторов, тем быстрее все пойдет".
И действительно, акторы легкие, и вы можете запускать миллионы из них на одной виртуальной машине. Да, вы можете. Но нужно ли?
Если вы можете, это не значит, что вы должны
Короткий ответ: не всегда – это зависит от того, что вы делаете с акторами.
Если в вашей системе много долгоживущих акторов, каждый из которых содержит немного состояния, и взаимодействуют друг с другом время от времени, вы вполне можете оказаться с миллионом акторов – и это законный вариант использования, очень хорошо поддерживаемый Akka. Например, можно создать систему с большим количеством пользователей, где каждый пользователь представлен актором. Чистый актор Akka занимает всего 300 байт памяти, поэтому вполне возможно создать миллионы на одной машине и оставить их работать, не беспокоясь ни о чем. И если в конечном итоге вы создадите много акторов или акторов с большим состоянием, что они больше не влезут в память одной машины, кластерное шардирование упростит распределение акторов по нескольким машинам.
Однако, если у вас есть несколько типов акторов, которые участвуют в вычислении чего-то – например, разбор XML-документа — сомнительно создавать миллионы таких акторов (не важно напрямую или через роутер).
Процессор имеет фиксированное количество ядер (аппаратных потоков) в своем распоряжении, и обработка сообщений акторами Akka выполняется в ExecutionContext, базирующемся на пуле потоков. По умолчанию это fork-join-executor, основанный на ForkJoinPool, добавленный в Java 7.
Но, несмотря на свое техническое преимущество, forkjoinpool не является волшебством, отменяющим законы физики. Если у вас есть один миллион акторов, каждый из которых анализирует XML-документ (уже загруженный в память) и 4 аппаратных потока, система не будет работать намного лучше, чем если бы у вас было только 4 актора, анализирующих эти XML-документы (при условии однородной нагрузки). Фактически, ваша система будет работать намного лучше всего с 4 акторами, потому что будут только минимальные накладные расходы с точки зрения планирования и управления памятью. Кстати, если в вашей системе всего несколько акторов, проверьте ваш пул потоков, который наверняка пытается повторно использовать один и тот же поток для одного и того же актора.
В общем, система не станет работать быстрее, если создать много акторов.
Акторы без состояния
Акторы объектно-ориентированы правильно (в отличие, скажем, от объектов в Java): их состояние не видно снаружи, и они общаются через сообщения. Невозможно сломать инкапсуляцию, потому что нельзя заглянуть в состояние актора во время его работы. В этом весь смысл акторов: они предоставляют вам иллюзию безопасного пространства, в котором сообщения выполняются последовательно, одно за другим, позволяя вам использовать изменяемое состояние внутри вашего актера, не беспокоясь о состоянии гонки (race conditions — прим. пер.) (ну, почти не беспокоясь: главное не позволить утечь стейту).
Вот почему использование акторов, у которых нет состояния, несколько странно, мягко говоря. За исключением акторов, которые управляют наблюдением за большими частями иерархии системы акторов (например, настройка резервных супервизоров), акторы действительно предназначены для работы с долгими вычислениями, которые имеют состояние. Говоря долгими я имею в виду, что актор будет обрабатывать несколько сообщений в течении своей жизни, производя разные результаты в зависимости от своего состояния, в отличие от одноразовых вычислений. Для них фьючеры являются отличной абстракцией: они позволяют асинхронное выполнение кода, идеально подходит для случаев работы с сетью или связанных с диском вычислений (или действительно интенсивных задач процессора), могут в композицию и имеют механизм обработки отказов. Они также хорошо интегрируются с акторами Akka (используя паттерн pipe).
В общем: не используйте акторы, если у вас нет состояния — они для этого не предназначены.
Комментарии (4)
ggo
22.08.2018 10:00Поэтому при проектировании систем акторов надо думать о том, что на самом деле должно быть асинхронным, в основном это:
— вызовов внешних систем (за пределами вашей jvm)
— вызовы блокирующих операций (устаревшие API, тяжелые вычисления, …)
Да
В общем, система не станет работать быстрее, если создать много акторов.
Да
В общем: не используйте акторы, если у вас нет состояния — они для этого не предназначены.
Тут надо добавить:
В общем: не используйте акторы,
— если у вас нет состояния, за которое возникает конкуренция
— не нужно горизонтально масштабироваться
Mishiko
Откуда взяли картинку? Впервые за день рассмеялся
MaxVetrov
Только из-за нее и зашел =) Нарушены все меры безопасности. Хотя один мужик в наушниках =) Авось пронесет!
Maa-Kut
Чтобы не слышать воплей коллеги-верстака, не иначе.