Akka Actor - это среда параллелизма, построенная на Scala и Java, цель которой - предоставить простую и понятную абстракцию для моделирования ваших компонентов, отделяя многопоточность от бизнес-логики.

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

Akka Actor также предлагает эффективный параллелизм для вашей системы. Потоки и планирование работы управляются самим Akka, где Akka использует потоки только на тех акторах, у которых есть реальная работа для определенных субъектов, следовательно, не удерживая потоки, ожидающие и тратя ресурсы. Связь между акторами обычно осуществляется посредством отправки сообщений в почтовый ящик целевого актора, и актор будет обрабатывать сообщения из своего почтового ящика последовательно в порядке FIFO. Поэтому мы вполне естественно можем моделировать нашу систему в асинхронном стиле. (Но Akka действительно предлагает подходы для синхронного общения, если вам нужно.)

Теперь давайте посмотрим на исходный код, чтобы понять, как это работает.

Базовая функциональность определяется в trait с именем Actor. Основной метод - receive, именно здесь мы определяем, как субъект реагирует на каждый тип сообщения, которое он поддерживает. Это частичная функция, поэтому вы можете реализовать каждый тип сообщения для case и добавлять новые case при появлении новых типов сообщений. Эта черта также предоставляет методы перехвата жизненного цикла, позволяющие настроить управление жизненным циклом для актера.

Обычно мы должны обращаться к Actor только через его ActorRef, который относится к реализации Actor и предоставляет некоторые общие функции, такие как путь к актору, метод отправки / пересылки сообщения этому Actor и т. Д. ActorRef дополнительно инкапсулируется в ActorCell, который содержит контекст вокруг актера, в том числе:

  • ActorSystem это в
  • ActorRef относится к самому себе
  • объект Props, использованный при создании Actor
  • MessageDispatcher, который управляет планированием работы акторов с пулом потоков.
  • ActorRef своему родительскому актеру
  • behaviorStack для хранения набора поведений, определенных для актера в порядке LIFO
  • a sysmsgStash для временного хранения системных сообщений

ActorCell также имеет доступ к MailBox, связанному с его актером. Это определяется чертой dungeon.Dispatch.

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

Актеры бегут в ActorSystem. Первым делом нужно запустить ActorSystem до того, как мы сможем порождать внутри нее акторов. Реализация находится в классе ActorSystemImpl.

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

Вызов метода ActorSystem.actorOf(props: Props) создаст актера верхнего уровня. Он делегирует реальное создание ActorCell’s ActorRefProvider, затем вызывает ActorRef.start() для начальной загрузки актора и возврата ссылки на актера.

Тогда что делает ActorRef.start()? Он делегирует вызов dungeon.Dispatch, одной из черт, расширяющих ActorCell.

В нем говорится: «Начните актера с планирования его почтового ящика», а не «планируйте ячейку актера»?

Оказывается, что Mailbox, а не Actor, является задачей, переданной в пул потоков или в пул разветвления. Mailbox.run() показывает, что при выполнении почтовый ящик сначала проверяет, закрыт ли сам, если нет, то обрабатывает все системные сообщения, а затем обрабатывает сообщения в очереди почтового ящика. Когда он завершит обработку сообщений, нам нужно перепланировать почтовый ящик для следующего выполнения. Оно будет повторно отправлено на ExecutionService, если в почтовом ящике остались необработанные системные сообщения или сообщения почтового ящика.

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

Вот что происходит, когда мы создаем и начинаем нового актера. Далее мы увидим, как сообщение отправляется актеру.

Вы можете отправить любой неизменяемый объект в виде сообщения ActorRef, вызвав метод !, как показано ниже:

actorA ! DoA("details...")

затем метод ! вызывает sendMessage в соответствующей ячейке ActorCell, что реализовано в трейте dungeon.Dispatch.

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

При чтении исходного кода я получил следующие основные сведения:

  • Платформа Akka Actor использует ExecutorServices для одновременного планирования и выполнения сообщений в почтовых ящиках экземпляров акторов.
  • Это почтовый ящик, который реализует Runnable и выполняется ExecutorServices. При выполнении каждый почтовый ящик применяет соответствующую функцию Actor.receive к сообщениям в почтовом ящике в подходе FIFO.
  • Вы можете думать об акторе как о Runnable задаче, которая имеет очередь для хранения отправленных ему сообщений и определяет поведение в отношении того, как обрабатывать каждый тип сообщений, которые он поддерживает. Задачи могут общаться только через отправку сообщений.
  • Отправка сообщений происходит одновременно без гарантии упорядочивания, но сообщения, полученные конкретным субъектом, будут обрабатываться в последовательном порядке.