Akka Actors - очень популярный и широко используемый фреймворк, который реализует акторов (или, для простоты, облегченные потоки) на JVM. Чтобы понять, как работают Актеры и как они сопоставляются с потоками JVM, мы создали нашу собственную крошечную структуру Акторов, похожую на Akka Actors. Основное внимание уделяется изучению и пониманию планирования Актеров, поэтому многие другие функции, включая реализацию супервизора и т. Д., Опускаются.

Пример кода доступен по адресу https://github.com/unmeshjoshi/actorscheduling.

Ниже приведены основные концепции любой структуры акторов.

Актеры

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

Почтовые ящики

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

Сообщения в почтовом ящике отправляются Актеру по одному по порядку.

Диспетчеры

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

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

Чтобы реализовать Актера, нам нужна функция, обрабатывающая сообщения. Что-то вроде следующего

Тогда Actor может быть реализован примерно так:

Для выполнения этого Актера нам нужен способ прикрепить к нему почтовый ящик, а также связать его с диспетчерами. Это можно сделать, создав оболочку над Actor, примерно так:

Как можно видеть, ActorCell может быть универсальным объектом, который принимает реализацию Class for Actor и сохраняет ссылку на свой метод получения.

Важно отметить, что мы не можем предоставить эту внутреннюю реализацию клиенту Актера. Как этого добиться?

Как мы обсуждали выше, единственный способ общаться с Актером - это отправить ему сообщение.

Мы можем определить этот контракт следующим образом

А затем реализуйте ActorRef следующим образом

Как клиенты получают этот ActorRef?

Мы можем сделать это, используя метод Factory для создания актера, как показано ниже.

ActorSystem - это фабрика, которая связывает воедино ActorRefs, Dispatchers и Mailboxes.

Теперь клиенты могут создавать экземпляры Actor следующим образом

Почтовые ящики и диспетчеры вступают в игру, когда сообщение отправляется Актеру. Как мы обсуждали выше, ActorRef - это оболочка вокруг ActorCell, которая связывает экземпляры MailBox и Actor вместе.

Когда клиент отправляет сообщения в ActorRef, они направляются в ActorCell следующим образом:

ActorCell поддерживает ссылки на MailBox и Dispatcher. Сообщение отправляется диспетчеру немедленно.

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

Служба исполнителя в случае актеров Akka - это не что иное, как ForkJoinPool. Существует много информации о том, как работает ForkJoinPool и почему он предпочтителен для получения высокого уровня эффективности на многоядерных машинах. (Http://gee.cs.oswego.edu/dl/papers/fj.pdf)

MailBox реализует ForkJoinTask, а также поддерживает параллельную очередь для хранения сообщений.

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

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

Здесь интересно отметить концепцию пропускной способности. Конфигурация пропускной способности определяет, сколько сообщений актер может обработать, когда он запланирован для выполнения в потоке.

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

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