akka/akka-core

weird behavior when a classic actor ask a typed actor

Open

#29,156 建立於 2020年5月30日

在 GitHub 查看
 (7 留言) (0 反應) (0 負責人)Scala (3,547 fork)batch import
1 - triagedhelp wantedt:docs

倉庫指標

Star
 (13,277 star)
PR 合併指標
 (平均合併 8天 19小時) (30 天內合併 10 個 PR)

描述

akka version: 2.6.4

I am changing an actor in my project from classic to typed, and run into a weird issue regarding ask between classic and typed actors.

Currently, I have 2 classic actors: Pinger and Ponger. Ponger does only 1 thing: echo back the id and a fixed value 0. Pinger will ping the Ponger on load.

import akka.actor.Scheduler
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.adapter._
import akka.actor.typed.{ActorRef, ActorSystem, Behavior}
import akka.pattern.{RetrySupport, ask, pipe}
import akka.util.Timeout
import akka.{actor => classic}

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.util.Random

object ClassicAskClassic extends App {
  val system: classic.ActorSystem = classic.ActorSystem("ClassicAskClassic")
  val ponger = system.actorOf(classic.Props(new Ponger()), "Ponger")
  val pinger =
    system.actorOf(classic.Props(new Pinger(ponger)), "Pinger")

  class Pinger(ponger: classic.ActorRef)
      extends classic.Actor
      with classic.ActorLogging
      with RetrySupport {

    override def preStart(): Unit = {
      super.preStart()
      self ! Pinger.Initialize
    }

    override def receive: Receive = {
      case Pinger.Initialize =>
        implicit val timeout: Timeout = Timeout(1.second)
        implicit val scheduler: Scheduler = context.system.scheduler
        implicit val ec: ExecutionContext = context.dispatcher
        retry(
          () => {
            val id = (new Random).nextInt(1000)
            log.info(s"Ping id=$id!")
            ponger.ask(Ponger.Ping(id)).mapTo[Ponger.Pong]
          },
          10,
          1.second
        ).pipeTo(self)

      case Ponger.Pong(id, v) =>
        log.info(s"Got a Pong id=$id, v=$v!")
        log.info("=========================")
    }
  }

  class Ponger extends classic.Actor with classic.ActorLogging {
    override def receive: Receive = {
      case Ponger.Ping(id) =>
        sender() ! Ponger.Pong(id, 0)
    }
  }

  object Pinger {
    case object Initialize
  }

  object Ponger {
    case class Ping(id: Int)
    case class Pong(id: Int, v: Int)
  }
}

Run the program, the console output is like:

18:18:44.994 [ClassicAskClassic-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskClassic$Pinger - Ping id=45!
18:18:44.997 [ClassicAskClassic-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskClassic$Pinger - Got a Pong id=45, v=0!
18:18:44.998 [ClassicAskClassic-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskClassic$Pinger - =========================

This is expected: the ask succeeds in one attempt, and that's it.

However, if I switch the Ponger into typed actor, the behavior becomes weird:

import akka.actor.Scheduler
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.adapter._
import akka.actor.typed.{ActorRef, ActorSystem, Behavior}
import akka.pattern.{RetrySupport, ask, pipe}
import akka.util.Timeout
import akka.{actor => classic}

import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import scala.util.Random

object ClassicAskTyped extends App {
  val system: classic.ActorSystem = classic.ActorSystem("ClassicAskTyped")
  val typedSystem: ActorSystem[Nothing] = system.toTyped
  val ponger = system.spawn(Ponger(), "Ponger")
  val pinger =
    system.actorOf(classic.Props(new Pinger(ponger.toClassic)), "Pinger")

  class Pinger(ponger: classic.ActorRef)
      extends classic.Actor
      with classic.ActorLogging
      with RetrySupport {

    override def preStart(): Unit = {
      super.preStart()
      self ! Pinger.Initialize
    }

    override def receive: Receive = {
      case Pinger.Initialize =>
        implicit val timeout: Timeout = Timeout(1.second)
        implicit val scheduler: Scheduler = context.system.scheduler
        import akka.actor.typed.scaladsl.adapter._
        implicit val ec: ExecutionContext = context.dispatcher
        retry(
          () => {
            val id = (new Random).nextInt(1000)
            log.info(s"Ping id=$id!")
            ponger.ask(Ponger.Ping(id, self)).mapTo[Ponger.Pong]
          },
          10,
          1.second
        ).pipeTo(self)

      case Ponger.Pong(id, v) =>
        log.info(s"Got a Pong id=$id, v=$v!")
        log.info("=========================")
    }
  }

  object Pinger {
    case object Initialize
  }

  object Ponger {

    def apply(): Behavior[Command] = {
      Behaviors.receive { (context, message) =>
        message match {
          case Ping(id, replyTo) =>
            context.log.info(s"Processing Ping id=$id!")
            replyTo ! Pong(id, 0)
            Behaviors.same
        }
      }
    }

    sealed trait Command

    sealed trait Reply

    case class Ping(id: Int, replyTo: ActorRef[Reply]) extends Command

    case class Pong(id: Int, v: Int) extends Reply

    private case class Set(v: Int) extends Command
  }
}

The console output:

18:26:52.557 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=206!
18:26:52.561 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=206!
18:26:52.562 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=206, v=0!
18:26:52.562 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:26:54.593 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=356!
18:26:54.593 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=356!
18:26:54.593 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=356, v=0!
18:26:54.593 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:26:56.634 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=374!
18:26:56.634 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=374!
18:26:56.635 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=374, v=0!
18:26:56.635 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:26:58.673 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=375!
18:26:58.673 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=375!
18:26:58.673 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=375, v=0!
18:26:58.673 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:00.712 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=402!
18:27:00.712 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=402!
18:27:00.713 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=402, v=0!
18:27:00.713 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:02.754 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=729!
18:27:02.754 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=729!
18:27:02.754 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=729, v=0!
18:27:02.754 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:04.793 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=406!
18:27:04.793 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=406!
18:27:04.794 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=406, v=0!
18:27:04.794 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:06.833 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=663!
18:27:06.833 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=663!
18:27:06.833 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=663, v=0!
18:27:06.833 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:08.871 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=646!
18:27:08.871 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=646!
18:27:08.872 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=646, v=0!
18:27:08.872 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:10.913 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=521!
18:27:10.913 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=521!
18:27:10.914 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=521, v=0!
18:27:10.914 [ClassicAskTyped-akka.actor.default-dispatcher-6] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:12.952 [ClassicAskTyped-akka.actor.default-dispatcher-4] INFO  akkaactors.ClassicAskTyped$Pinger - Ping id=944!
18:27:12.952 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Ponger$ - Processing Ping id=944!
18:27:12.953 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - Got a Pong id=944, v=0!
18:27:12.953 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akkaactors.ClassicAskTyped$Pinger - =========================
18:27:13.981 [ClassicAskTyped-akka.actor.default-dispatcher-5] INFO  akka.actor.RepointableActorRef - Message [akka.actor.Status$Failure] from Actor[akka://ClassicAskTyped/user/Pinger#1224703828] to Actor[akka://ClassicAskTyped/user/Pinger#1224703828] was unhandled. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

It runs out of 10 retries.

Can anyone help? Thx

貢獻者指南