Каковы опасности использования объекта в качестве актера АККА? - PullRequest
0 голосов
/ 07 сентября 2018

В приведенном ниже коде я использую AKKA-объект MonitorActor, даже если это объект. Я никогда не вижу этот шаблон в рабочем коде, хотя, кажется, он работает хорошо.

Имеет ли приведенный ниже код проблемы параллелизма в результате использования объекта в качестве субъекта?

Есть ли здесь какие-нибудь "попавшиеся" актёры из AKKA?

case class SomeEvent(member: String)

class Example(eventBus: EventBus)(implicit actorSystem: ActorSystem) {

  val members: AtomicReference[Set[String]] = new AtomicReference(Set())

  actorSystem.actorOf(Props(MonitorActor))

  private object MonitorActor extends Actor {

    eventBus.subscribe(classOf[SomeEvent])
    var isEnough = false

    override def receive: Receive = {
      case SomeEvent(member: String) =>
          val newMembers = members.updateAndGet(_ + member)
          if (newMembers.size >= 10) {
            isEnough = true
          }
        }
    }
}

1 Ответ

0 голосов
/ 07 сентября 2018

Непосредственный вопрос, возникающий из этого «паттерна»: что произойдет, если Actor будет добавлен к actorSystem дважды:

actorSystem.actorOf(Props(MonitorActor))
actorSystem.actorOf(Props(MonitorActor))

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

В лучшем случае каждый SomeEvent обрабатывается дважды по одной и той же логике. В худшем случае вы попадете в неприятные условия гонки с isEnough. Итак, давайте предположим, лучший случай.

Даже в лучшем случае каждый SomeEvent будет обрабатываться по той же логике. Это неплохо в примере вопроса, потому что members - это Set. Но если бы это был List, вы бы начали получать двойные вставки одного и того же события.

Другая проблема заключается в том, чтобы защищать себя от условий гонки, связанных с members. Хорошая причина для members быть AtomicReference состоит в том, чтобы разрешить ситуацию, когда два «независимых» субъекта пытаются получить доступ к members одновременно. Но это идет вразрез со всей целью актерской модели. Из оригинального формализма 1973 года (выделено мной):

Архитектура является общей по отношению к структуре управления и делает не иметь или не нуждаться в примитивах goto, interrupt или семафор .

Аналогичное описание можно найти во введении к документации akka (выделено мной):

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

Итак, мы фактически нарушили каркас модели Actor, и все, что мы получили, - это не необходимость вызывать конструктор. Сравните код примера вопроса с «предпочтительной» реализацией:

class MonitorActor() extends Actor { 

  val members: Set[String] = Set.empty[String]

  eventBus.subscribe(classOf[SomeEvent])
  var isEnough = false

  override def receive: Receive = {
    case SomeEvent(member: String) => {
      members add member
      isEnough = members.size >= 10
    }
  }
}

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

...