Akka TypedActor против написания собственного статического интерфейса для класса Actor

Я использую Akka и Scala около месяца, и меня несколько беспокоит замена явных интерфейсов сообщениями. Рассмотрим следующего простого актера Akka:

case class DoMyHomework()
class Parent extends Actor {
  def receive = {
    case d: DoMyHomework => // do nothing
  }
}

Актерский или неактерский код, который отправляет этому актеру сообщение DoMyHomework, подобное этому:

ActorRef parent = ...
parent.ask(DoMyHomework)

Не имеет ни малейшего представления о том, каков будет результат. Какой ответ? Получу ли я когда-нибудь ответ? Могу ли я получить исключение? И так далее.

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

Пытаясь немного очистить это, я подумал о следующем:

trait SomeoneSmarter {
  def wouldYouDoMyHomework: Future[Boolean] 
}
class Parent extends Actor with SomeoneSmarter {
  case class DoMyHomework()
  def wouldYouDoMyHomework = {
    (self ? DoMyHomework()).mapTo(Boolean)
  }
  def receive = {
    case d: DoMyHomework =>
      // TODO: If I'm busy schedule a false "No way" reply for a few seconds from now.
      // Just to keep their hopes up for a while. Otherwise, say sure right away.
  }
}

Итак, я поговорил с коллегами об этом, и одна из реакций была «вы не соответствуете актерской модели».

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

Актеры, которых я предлагаю, по-прежнему имеют возможность отправлять сообщения между собой, подписываться на потоки событий и все то, что вы ожидаете от Akka. А интерфейс дает вам проверенный временем способ узнать, о чем вы говорите. И это помогает при кодировании в IDE и т. Д. И почему пользователь актера должен знать, что это актер (если только он не актер и не очень тесно с ним связан)?

Другая реакция, которую я получил, была «похоже, вам нужен TypedActor». Но, прочитав о TypedActor, я в этом не убедился. Безусловно, TypedActor избавляет меня от необходимости создавать эти внутренние сообщения. Но, по крайней мере, из образца кода на http://doc.akka.io/docs/akka/snapshot/scala/typed-actors.html У меня сложилось впечатление, что TypedActor предназначен только для работы в качестве прокси-сервера для блока кода, который вы хотите инкапсулировать или сделать поток- безопасно или просто не вызывать напрямую из текущего потока. А то, что вы кодируете, - это просто реализация и интерфейс. Вы не связываетесь с самим актером (прокси) - например, если вы хотите, чтобы ваша реализация выполняла периодическую работу или подписывалась на поток событий, или делала что-либо еще, не связанное с интерфейсом.

Я также читал http://letitcrash.com/post/19074284309/when-to-use-typedactors и не нашел этот пример более показательным. Я, наверное, просто не в восторге от TypedActor (не то чтобы я еще не утверждал, что действительно разбирался в актерах).

заранее спасибо за помощь.

Пино


person Pino    schedule 20.09.2012    source источник
comment
В списке рассылки Akka есть сообщение, в котором описывается использование класса-оболочки для поддержки операторов ввода, хотя здесь ввод не охватывает всего, что обсуждает Пино: groups.google.com/forum / #! topic / akka-user / THMgHyO21To   -  person Mark Butler    schedule 28.01.2014
comment
Здесь есть интересный пост, в котором содержится критика акторов Akka, которые не могут предоставить типы: noelwelsh.com/programming/2013/03/04/   -  person Mark Butler    schedule 30.01.2014


Ответы (3)


Актер инкапсуляция

Позвольте мне сначала ответить на один момент, который я считаю очень важным. Ты говоришь:

И почему пользователь актера должен знать, что это актер (если только он не актер и не очень тесно с ним связан)?

Акторы - это радикально отличная парадигма программирования от традиционного объектно-ориентированного программирования, основное отличие состоит в том, что все асинхронно и, следовательно, никогда не бывает реальных «возвращаемых значений». Это означает, что скрывать тот факт, что он является действующим лицом, обычно - плохая идея, поскольку исключения относятся к моей Сообщение в блоге TypedActors. Лучшее в акторах - это то, что они полностью инкапсулированы - в Akka за ActorRef - в отличие от слабой инкапсуляции объектно-ориентированных языков. Чтобы получить от этого максимальную отдачу, по возможности выставляйте ActorRefs, что дает клиентскому коду возможность использовать их наиболее подходящим способом (который может использовать tell или ask в зависимости от контекста).

Структурирование ваших сообщений

При написании актера вы должны собрать все об этом актере в одном месте, включая описание контракта интерфейса. Это могло выглядеть примерно так:

object Parent {
  /**
   * Send this message to make your parent do your homework … yeah, right ;-)
   */
  case object DoHomework
}

/**
 * This actor will do your homework if asked to.
 * 
 * ==Actor Contract==
 * 
 * ===Inbound Messages===
 *  - '''DoHomework''' will ask to do the homework
 * 
 * ===Outbound Messages===
 *  - '''HomeworkResult''' is sent as reply to the '''DoHomework''' request
 * 
 * ===Failure Modes===
 *  - '''BusinessTripException''' if the parent was not home
 *  - '''GrumpyException''' if the parent thinks you should do your own homework
 */
class Parent extends Actor {
  …
}

Отличия от TypedActor

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

person Roland Kuhn    schedule 30.09.2012

Отказ от ответственности: я не эксперт по Akka / Actors. Я работаю с Actors и Akka около 18 месяцев и все еще пытаюсь осмыслить определенные концепции, особенно когда не использовать Akka.

Для конкретного и узкого случая, когда вы хотите узнать тип возвращаемого значения Akka future, да, вам следует использовать TypedActor. Несколько раз я использовал TypedActors, они использовались для предоставления API для модуля, выходящего за пределы системы Actor. То есть я построил систему поверх Akka, которая выполняла большую часть своей работы внутри сети Akka, но имела один или два модуля за пределами сети Akka, которым требовался доступ к функциям, предоставляемым сетью Akka. Самым примечательным был интерфейс Scalatra, который звонил в сеть Akka и выполнял некоторую работу со значениями, возвращаемыми сетью Akka, прежде чем отвечать своему клиенту. Однако TypedActor на самом деле был всего лишь интерфейсом сети Akka. Я рассматриваю использование TypedActor в качестве интерфейса API для внешних (внешних по отношению к сети Akka) модулей как еще одно разделение проблем.

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

Если бы я реализовал описанную вами функцию, это выглядело бы следующим образом:

trait SomeoneSmarter {

  def wouldYouDoMyHomework : Boolean 

}

class Response()
case class NoWay() extends Response
case class Sure() extends Response

class ActorNetworkFrontEnd extends Actor {

  def receive = {
    case d: DoMyHomework =>
      busy match {
        case true => sender ! NoWay()
        case false => sender ! Sure()
      }
  }
}

case class SomeoneSmarter(actorNetworkFrontEnd:ActorRef) extends SomeoneSmarter {

  def wouldYouDoMyHomework : Boolean = {
    val future = actorNetworkFrontEnd ? DoMyHomework()
    val response = Await.result(future, timeout.duration).asInstanceOf[Response]
    response match {
      case NoWay() => false
      case Sure() => true
    }
  }

}

Имейте в виду, как я написал wouldYouDoMyHomework, он будет блокироваться в ожидании ответа. Однако есть умные способы сделать это асинхронно. См. http://doc.akka.io/docs/akka/2.0.3/scala/futures.html для получения дополнительной информации.

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

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

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

person Eric Reichert    schedule 21.09.2012
comment
Спасибо за ответ. Больше пищи для размышлений. И извините, что потратил время, чтобы вернуться к вам (у нас были трехдневные выходные здесь, в Барселоне, где я живу). - person Pino; 25.09.2012
comment
почему я не вижу ничего производного от TypedActor? - person Chris DaMour; 08.11.2013

Модель вычислений Actor имеет огромное сходство с объектно-ориентированным программированием. ОО - это косвенная передача управления. Когда вы вызываете метод (отправляете сообщение), вы теряете контроль над сообщением. Конечно, статические языки программирования немного помогут вам со всеми хорошими качествами проверки типов, но в остальном вы понятия не имеете, что произойдет с сообщением. Черт, может быть, метод никогда не вернется, хотя тип возвращаемого значения явно говорит, что вернется (выброшенное исключение, живая блокировка, вы называете это ...)! Согласен, когда вы привыкли к Java или даже к лучшему Scala, отказываться от статической типизации - отстой, но это не значит, что вы не получаете никаких преимуществ. Динамический набор текста дает вам слабую связь. Например, нет необходимости создавать дополнительные интерфейсы только для того, чтобы ввести фиктивный актер для ваших тестов; ActorRef - единственный API, который вам нужен.

person agilesteel    schedule 21.09.2012
comment
Я ценю вашу точку зрения о передаче контроля, я думаю, что это хорошо. Но я по-прежнему считаю, что интерфейсы полезны даже просто для лучшей читаемости кода, например. кем-то незнакомым с кодовой базой. - person Pino; 25.09.2012