Подтвердить что ты не робот

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

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

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 предназначен только для работы в качестве прокси-сервера вокруг блока кода, который вы хотите инкапсулировать, или создания потока -safe или просто не вызывать прямо из вашего текущего потока. И то, что вы кодируете, - это просто реализация и интерфейс. Вы не возитесь с самим актером (прокси) - например. если вы хотите, чтобы ваша реализация выполняла периодическую работу или подписывалась на поток событий или делала что-либо еще, не связанное с интерфейсом.

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

заблаговременно за помощь.

Пино

4b9b3361

Ответ 1

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

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

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

Актеры представляют собой радикально отличающуюся парадигму программирования от обычного OO, основное отличие состоит в том, что все асинхронно и, следовательно, никогда не существует реальных "возвращаемых значений". Это означает, что обычно сложно скрывать тот факт, что это актер, поскольку исключения относятся к моему сообщению в блоге TypedActors. Самое лучшее в актерах в том, что они полностью инкапсулированы - в Akka позади ActorRef - в отличие от слабой инкапсуляции языков OO. Чтобы получить максимальную отдачу от этого, выведите ActorRef, где это возможно, что дает клиентскому коду возможность использовать их наиболее подходящим способом (который может использовать 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 о добавлении некоторой магии, но это должно ждать, пока она не сможет получить приоритет). Важная часть имеет все свойства об актере, определенном в одном месте.

Ответ 2

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

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

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

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

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
    }
  }

}

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

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

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

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

Ответ 3

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