0Pricing
Scala for Backend Engineering & Functional Programming · Урок

Проектирование акторных систем

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

«Проектирование акторных систем» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 2 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 3 уроков всего.

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

Добро пожаловать на урок 2! На предыдущем уроке мы изучили основы модели акторов Akka. Теперь давайте подробно рассмотрим проектирование систем акторов Akka.

Хорошо спроектированная система акторов надёжна, масштабируема и удобна в сопровождении. Мы рассмотрим, как структурировать акторы, определять их поведение и обеспечивать эффективный обмен данными между ними.

Устройство иерархий акторов

Акторы Akka организованы в древовидную иерархию, подобную файловой системе. У каждого актора, кроме «корневого» актора, есть родитель.

  • Связь родитель—потомок: Актор, создающий другой актор, становится его родителем.
  • Надзор: Эта иерархия крайне важна для отказоустойчивости Akka. Родители отвечают за надзор над своими потомками (подробнее об этом — в следующем уроке!).
  • Адресация по пути: У каждого актора есть уникальный путь, отражающий его положение в иерархии, например /user/parentActor/childActor.

Настройка ActorSystem

ActorSystem — точка входа для всех приложений Akka. Она управляет ресурсами, необходимыми акторам, включая пулы потоков и планировщики.

Вы создаёте её один раз для каждого приложения, а затем используете для создания акторов верхнего уровня. Это своего рода контейнер для всего приложения акторов.

Попробуйте запустить этот простой код для создания ActorSystem:

import akka.actor.ActorSystem

object AkkaSystemApp {
  def main(args: Array[String]): Unit = {
    // Create an ActorSystem with a unique name
    val system = ActorSystem("MyFirstActorSystem")
    println("ActorSystem created: " + system.name)

    // Remember to terminate the system when done!
    system.terminate()
  }
}

Определение поведения актора

Поведение актора определяет его реакцию на различные сообщения. В Scala оно определяется путём расширения трейта Actor и реализации метода receive.

  • Метод receive — это частичная функция, сопоставляющая входящие сообщения с определёнными действиями.
  • Каждый актор обрабатывает сообщения по одному, последовательно, что обеспечивает потокобезопасность его внутреннего состояния.

ActorRef: адрес актора

Вы не взаимодействуете с экземплярами акторов напрямую. Вместо этого Вы отправляете сообщения в ActorRef. Представляйте ActorRef как почтовый адрес актора.

  • Это дескриптор, представляющий определённый актор внутри системы.
  • Сообщения, отправленные в ActorRef, помещаются в почтовый ящик актора.
  • Вы получаете ActorRef при создании актора с помощью actorOf.

Отправка сообщений акторам

Основной способ взаимодействия акторов — отправка сообщений. Чаще всего сообщение отправляют с помощью метода ! (произносится «tell»).

receiverActorRef ! "Your message here"

Метод tell является асинхронным и работает по принципу «отправил и забыл». Он отправляет сообщение и не ждёт ответа.

Объединим создание актора и отправку ему сообщений:

import akka.actor.{Actor, ActorRef, Props, ActorSystem}

// 1. Define the actor's behavior
class GreeterActor extends Actor {
  def receive = {
    case "hello" => println(s"Greeter: Hello from ${self.path.name}!")
    case msg: String => println(s"Greeter: Received message: '$msg'")
    case _ => println("Greeter: Received something unexpected!")
  }
}

object SimpleActorApp {
  def main(args: Array[String]): Unit = {
    // 2. Create the ActorSystem
    val system = ActorSystem("GreeterSystem")

    // 3. Create a top-level actor and get its ActorRef
    val greeter: ActorRef = system.actorOf(Props[GreeterActor], "myGreeter")
    
    // 4. Send messages to the actor using '!'
    greeter ! "hello"
    greeter ! "How are you doing today?"
    greeter ! 123 // This will match the _ case

    // Give actors time to process messages, then terminate
    Thread.sleep(1000)
    system.terminate()
  }
}

Создание дочерних акторов

Акторы могут создавать другие акторы, формируя рассмотренную нами иерархию. Родительский актор создаёт дочерние акторы с помощью context.actorOf.

  • context.actorOf: используется актором для создания потомков.
  • system.actorOf: используется приложением для создания акторов верхнего уровня (стражей).

Создание дочерних акторов помогает организовать систему, делегировать задачи и изолировать сбои.

Взаимодействие родителя и потомка

Акторы взаимодействуют, отправляя сообщения в ActorRef друг друга. Дочерний актор может получить ActorRef родителя через context.parent. Чтобы ответить отправителю сообщения, актор может использовать sender().

Рассмотрим пример, в котором родитель создаёт потомка, поручает ему выполнить работу, а потомок сообщает о завершении:

import akka.actor.{Actor, ActorRef, Props, ActorSystem}
import scala.concurrent.ExecutionContext.Implicits.global

// Child Actor Definition: Does work and replies to sender
class ChildActor extends Actor {
  def receive = {
    case "work" =>
      println(s"ChildActor (${self.path.name}): Doing work from ${sender().path.name}")
      sender() ! "done" // Reply 'done' to the sender (parent)
  }
}

// Parent Actor Definition: Creates child, sends work, receives reply
class ParentActor extends Actor {
  // Create a child actor when the parent starts
  val child: ActorRef = context.actorOf(Props[ChildActor], "myChild")

  def receive = {
    case "start" =>
      println("ParentActor: Kicking off child's work.")
      child ! "work" // Tell the child to do 'work'
    case "done" =>
      println(s"ParentActor: Child ${sender().path.name} reported 'done'.")
      context.stop(child) // Stop the child actor
      context.stop(self)   // Stop the parent actor
  }
}

object HierarchyApp {
  def main(args: Array[String]): Unit = {
    val system = ActorSystem("HierarchySystem")
    val parent: ActorRef = system.actorOf(Props[ParentActor], "parentActor")
    parent ! "start" // Start the interaction

    // Wait for the system to terminate (actors will stop themselves)
    system.whenTerminated.onComplete(_ => println("ActorSystem terminated."))
  }
}

Корректная остановка акторов

Акторы следует корректно останавливать, когда они больше не нужны, чтобы освободить ресурсы. Остановить актора можно несколькими способами:

  • context.stop(self): актор останавливает себя сам.
  • context.stop(childRef): родитель останавливает одного из своих потомков.
  • system.stop(actorRef): используется для акторов верхнего уровня, хотя обычно останавливают всю систему.

При остановке актор обрабатывает все сообщения в своём почтовом ящике перед завершением. Его потомки также останавливаются рекурсивно.

Быстрая проверка: проектирование акторов

Рассмотрите следующий фрагмент кода актора Akka. Как MyChildActor должен отправить сообщение обратно своему создавшему его родителю MyParentActor, если MyParentActor был отправителем исходного сообщения «request»?

Повторение: проектирование систем акторов

Отличная работа! В этом уроке мы рассмотрели ключевые аспекты проектирования систем акторов Akka:

  • Акторы организованы в иерархии, которые необходимы для надзора и структурирования.
  • ActorSystem — точка входа приложения в Akka.
  • Вы определяете поведение актора с помощью метода receive.
  • ActorRef — адрес для отправки сообщений.
  • Акторы взаимодействуют асинхронно с помощью метода ! (tell).
  • Родители создают потомков с помощью context.actorOf, а потомки могут отвечать с помощью sender().
  • Акторов можно корректно остановить с помощью context.stop().

Далее мы подробнее рассмотрим мощные стратегии надзора Akka и способы корректной обработки сбоев!

Часто задаваемые вопросы

Урок «Проектирование акторных систем» бесплатный?

Да — полный текст урока «Проектирование акторных систем» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 3 уроков всего.

Чему я научусь в уроке «Проектирование акторных систем»?

Научитесь проектировать и реализовывать иерархии акторов, определять их поведение и отправлять и получать сообщения. Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?

Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 3.

Сколько времени занимает урок «Проектирование акторных систем»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?

Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Основы акторной модели Akka
  2. Проектирование акторных систем
  3. Супервизия и отказоустойчивость
← Назад к Scala for Backend Engineering & Functional Programming