Статьи

Обрабатывайте каждое событие в вашем приложении Akka

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

Акка и реактивные, основанные на событиях приложения являются новым подходом к созданию программного обеспечения. Мы активно используем Akka в нашем текущем Scala-проекте. События особенно подходят для наших сценариев использования, поскольку мы взаимодействуем с внешним API, который может быть медленным. Это может повредить пользовательский опыт при использовании традиционного синхронного подхода. Но, к счастью, наши запросы могут выполняться асинхронно, поэтому их передача актеру казалась хорошей идеей.

Когда вещи выходят из-под контроля

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

Давайте посмотрим на пример класса, это актер, обрабатывающий события, связанные с тегами изображения и комментариями:

01
02
03
04
05
06
07
08
09
10
11
12
class YourActor extends Actor {
    override def receive = {
        case event: ImageTagged =>
          doSomething()
        case event: OtherImageTaggedByFriend =>
          doSomething2()
        case event: MostMotedUserImage =>
          doSomething3()
        case event: MostCommentedFriendImageChosen =>
          doSomething4()
      }
}

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

СУХОЙ раствор, нарушающий

Есть одно простое решение, которое позволит обнаружить забытые обработчики. Мы можем добавить case _ к каждому актеру:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
class YourActor extends Actor {
    override def receive = {
        case event: ImageTagged =>
          doSomething()
        case event: OtherImageTaggedByFriend =>
          doSomething2()
        case event: MostMotedUserImage =>
          doSomething3()
        case event: MostCommentedFriendImageChosen =>
          doSomething4()
        case event: _ :
          logger.error("Received unknown event " + event.getClass.toString)
      }
}

И хотя это выглядит довольно хорошо для одного или двух акторов, добавление одного и того же фрагмента кода к нескольким акторам проблематично и нарушает принцип DRY. Но, что самое опасное, кто-то в вашей команде может забыть добавить его (как кто-то сказал: «Каждое ручное задание, которое можно забыть, будет забыто» ). Так, может быть, мы должны искать лучшее решение?

Реагируйте на ЛЮБОЕ необработанное событие

события должны-не-необработанные

К счастью, мы не застряли в нашем подверженном ошибкам подходе. Когда актер не может обработать событие, которое было ему передано, UnhandledMessage вызывается и публикуется в EventStream ActorSystem.

Таким образом, для обработки каждого забытого события мы могли бы создать слушателя и подписать его на EventStream:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
class UnhandledMessageListener extends Actor {
 
  val logger = LoggerFactory.getLogger(getClass)
 
 
  override def receive = {
    case message: UnhandledMessage =>
      logger.error(s"CRITICAL! No actors found for message ${message.getMessage}"))
 
      if (!Environment.isProduction) {
        // Fail fast, fail LOUD
        logger.error("Shutting application down")
        System.exit(-1)
      }
  }
}

И подписывающийся фрагмент кода:

1
2
3
4
val actorSystem = ActorSystem.create("projectActorSystem")
 
 val listener = actorSystem.actorOf(Props(new UnhandledMessageListener()))
 actorSystem.eventStream.subscribe(listener, classOf[UnhandledMessage])

вот и все. Теперь каждый раз, когда происходит событие, которое не было обработано актером, мы будем знать о нем, особенно когда приложение развернуто в непроизводственной среде!