Akka actor继承与context.become

时间:2018-02-15 16:54:21

标签: scala akka typesafe

尝试使用become方法更改子actor的状态时,我遇到了一个棘手的问题。我根据建议hereReceiving特征实现了actor继承:

trait Mcma extends Receiving with ActorLogging {
  val actorName: String

  /**
    * Simple method to be overridden when necessary
    * @param entity the response entity
    */
  protected def handleHttpOK(entity: ResponseEntity): Unit = log.info(s"$actorName got unhandled OK response")

  // Add http responses match if necessary
  receiver {
    case HttpResponse(StatusCodes.OK, _, entity, _) => handleHttpOK(entity)

    case resp @ HttpResponse(code, _, _, _) =>
      log.error(s"$actorName got response code: {}", code)
      // Discard the flow to avoid backpressure
      resp.discardEntityBytes()

    case e: Status.Failure => log.error(s"$actorName got failure: {}", e.cause.getMessage)

    case _ => log.warning(s"Unexpected message in $actorName")
  }
}

当我有一个实现此Mcma特征的actor并使用become模式更改其自己的状态时,会出现问题:

class Reseau(url: String, optSender: Option[ActorRef]) extends Mcma with Receiving {
  override val actorName: String = "ReseauActor"

  /**
    * The active method used to handle actor state change with the become helper
    * @param queryDataStr the query string for each call to ReseauActor
    * @return
    */
  def active(queryDataStr: String): Receive = {
    case s: String => context become active(s)
  }

  // Init the actor with empty query
  receiver(
    active("")
  )
}

上下文发生变化后,e_等默认继承的匹配案例将不再匹配。很可能是一个明显的问题......

[更新后的工作版本及Evgeny的答案] 使用上面提到的Receiving特征, Reseau actor成为

class Reseau(url: String, optSender: Option[ActorRef]) extends Mcma {
  override val actorName: String = "ReseauActor"

  override def preStart(): Unit = {
    super.preStart()
    // Init the actor with empty query
    context become receiver(active(""))
  }

  /**
    * The active method used to handle actor state change with the become helper
    *
    * @param queryDataStr the query string for each call to ReseauActor
    * @return
    */
  def active(queryDataStr: String): Receive = {

    case s: String => context become receiver(active(s))
  }
}

Mcma特征:

trait Mcma extends Receiving with ActorLogging {
  val actorName: String

  /**
    * Simple method to be overridden when necessary
    * @param entity the response entity
    */
  protected def handleHttpOK(entity: ResponseEntity): Unit = log.info(s"$actorName got unhandled OK response")

  // For http response handling
  final implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system))

  override def preStart(): Unit = log.info(s"$actorName started")

  override def postStop(): Unit = log.info(s"$actorName stopped")

  // Add http responses match if necessary
  addReceiver {
    case HttpResponse(StatusCodes.OK, _, entity, _) => handleHttpOK(entity)

    case resp @ HttpResponse(code, _, _, _) =>
      log.error(s"$actorName got response code: {}", code)
      // Discard the flow to avoid backpressure
      resp.discardEntityBytes()

    case e: Status.Failure => log.error(s"$actorName got failure: {}", e.cause.getMessage)

    case _ => log.warning(s"Unexpected message in $actorName")
  }
}

并且为了定义一个不需要改变其内部状态的actor,你可以这样做:

class TLRVJob1() extends Mcma {
  override val actorName: String = "TLRVJob1Actor"

  addReceiver {
    case t: Get => ???
  }
}

2 个答案:

答案 0 :(得分:1)

在这种情况下,最简单的解决方法可能是放弃使用become。也就是说,不是将queryDataStr状态编码为参数,而是将queryDataStr更改为var

class Reseau(url: String, optSender: Option[ActorRef]) extends Mcma with Receiving {
  override val actorName: String = "ReseauActor"

  var queryDataStr: Option[String] = None

  receiver {
    case s: String => queryDataStr = Option(s)
  }
}

答案 1 :(得分:1)

我建议修改初始Receiving特征:

trait Receiving extends Actor {
  var receivers: Receive = Actor.emptyBehavior
  def addReceiver(next: Actor.Receive): Unit = {receivers = receiver(next)}
  def receiver(next: Actor.Receive): Receive = {receivers orElse next}

  def receive: Receive = receivers
}

现在,您有两种情况,添加接收的永久部分并具有依赖于类的部分。在层次结构中使用receiver更改addReceiver,并初始化您的Reseau课程:而不是

receiver(
  active("")
)

将其移至preStart格式:

override def preStart(): Unit = {
  super.preStart()
  context become receiver(active(""))
}

并将active方法正文更改为

context become receiver(active(s))