8

SimpleActorメッセージを自分自身に送信することで定期的なタスクを実行するActor ( ) がある非常に単純な例があります。メッセージは、アクターのコンストラクターでスケジュールされます。通常の場合 (つまり、障害がない場合)、すべて正常に動作します。

しかし、アクターが障害に対処しなければならない場合はどうでしょう。別のアクター ( SimpleActorWithFault) がいます。このアクターには欠点がある可能性があります。この場合、例外をスローして自分で生成しています。障害が発生すると (つまり、SimpleActorWithFault例外がスローされると)、自動的に再起動されます。ただし、この再起動により、アクタ内のスケジューラが台無しになり、例外として機能しなくなります。また、障害が急速に発生すると、予期しない動作がさらに発生します。

私の質問は、そのような場合に障害に対処するための好ましい方法は何ですか? Tryブロックを使用して例外を処理できることを知っています。しかし、Try をスーパー クラスに配置できない別のアクターを拡張している場合や、例外的な障害がアクターで発生した場合はどうなるでしょうか。

import akka.actor.{Props, ActorLogging}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import akka.actor.Actor

case object MessageA

case object MessageToSelf


class SimpleActor extends Actor with ActorLogging {

  //schedule a message to self every second
  context.system.scheduler.schedule(0 seconds, 1 seconds, self, MessageToSelf)

  //keeps track of some internal state
  var count: Int = 0

  def receive: Receive = {
    case MessageA => {
      log.info("[SimpleActor] Got MessageA at %d".format(count))
    }
    case MessageToSelf => {
      //update state and tell the world about its current state 
      count = count + 1
      log.info("[SimpleActor] Got scheduled message at %d".format(count))

    }
  }

}


class SimpleActorWithFault extends Actor with ActorLogging {

  //schedule a message to self every second
  context.system.scheduler.schedule(0 seconds, 1 seconds, self, MessageToSelf)

  var count: Int = 0

  def receive: Receive = {
    case MessageA => {
      log.info("[SimpleActorWithFault] Got MessageA at %d".format(count))
    }
    case MessageToSelf => {
      count = count + 1
      log.info("[SimpleActorWithFault] Got scheduled message at %d".format(count))

      //at some point generate a fault
      if (count > 5) {
        log.info("[SimpleActorWithFault] Going to throw an exception now %d".format(count))
        throw new Exception("Excepttttttiooooooon")
      }
    }
  }

}


object MainApp extends App {
  implicit val akkaSystem = akka.actor.ActorSystem()
  //Run the Actor without any faults or exceptions 
  akkaSystem.actorOf(Props(classOf[SimpleActor]))

  //comment the above line and uncomment the following to run the actor with faults  
  //akkaSystem.actorOf(Props(classOf[SimpleActorWithFault]))

}
4

2 に答える 2

9

正しい方法は、危険な動作を独自のアクターにプッシュすることです。このパターンはエラー カーネル パターンと呼ばれます (Akka の同時実行性、セクション 8.5 を参照)。

このパターンは、アクターが保持する可能性のある不安定な状態に基づいてアクターを互いに区別する、非常に常識的な監督へのアプローチを表しています。

簡単に言えば、状態が貴重なアクターが失敗したり再起動したりしてはならないということです。貴重なデータを保持するすべてのアクターは保護され、危険な操作はスレーブ アクターに委譲されます。スレーブ アクターは、再起動された場合に、良いことだけが起こるようにします。

エラー カーネル パターンは、リスク レベルをツリーのさらに下に押し下げることを意味します。

こちらの別のチュートリアルも参照してください。

したがって、あなたの場合は次のようになります。

SimpleActor 
 |- ActorWithFault

ここでは のスーパーバイザーとしてSimpleActor機能します。アクターのデフォルトの監視戦略は、子を再起動し、のものをエスカレートすることです: http://doc.akka.io/docs/akka/snapshot/scala/fault-tolerance.htmlActorWithFaultException

エスカレートとは、アクター自体が再起動される可能性があることを意味します。本当に再起動したくないので、スーパーバイザー戦略をオーバーライドSimpleActorすることで、常に再起動し、ActorWithFaultエスカレートしないようにすることができます。

class SimpleActor {
  override def preStart(){
    // our faulty actor --- we will supervise it from now on
    context.actorOf(Props[ActorWithFault], "FaultyActor") 
  ...

  override val supervisorStrategy = OneForOneStrategy () {
    case _: ActorKilledException => Escalate
    case _: ActorInitializationException => Escalate
    case _ => Restart // keep restarting faulty actor
  }

}
于 2014-04-16T15:01:45.980 に答える