
Акка и реактивные, основанные на событиях приложения являются новым подходом к созданию программного обеспечения. Мы активно используем 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]) |
вот и все. Теперь каждый раз, когда происходит событие, которое не было обработано актером, мы будем знать о нем, особенно когда приложение развернуто в непроизводственной среде!
| Ссылка: | Обрабатывайте каждое событие в вашем приложении Akka от нашего партнера JCG Томаша Дзюрко в блоге Code Hard Go Pro . |
