0Pricing
Scala for Backend Engineering & Functional Programming · レッスン

スーパービジョンと耐障害性

Akkaのスーパービジョン戦略を学び、障害から適切に復旧できる堅牢なシステムを構築します。

「スーパービジョンと耐障害性」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン3/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全3レッスンが含まれています。

レジリエントなシステムを構築する

並行アプリケーションでは、問題が発生する可能性があります。アクターがクラッシュしたり、ネットワーク呼び出しが失敗したり、データベースに接続できなかったりすることがあります。

フォールトトレランスとは、システムの一部に障害が発生しても、システムが正しく動作し続ける能力です。Akkaには、これを実現するための強力な仕組みが用意されています。

親子スーパービジョン

Akkaのアクターは、家系図のような階層で構成されます。あるアクターが別のアクターを作成すると、作成した側が親になります。

  • 親は子を監督する責任を負います。
  • 子アクターに障害が発生すると、親は通知を受け取ります。
  • その後、親が子の障害への対処方法を決定します。これをスーパービジョン戦略と呼びます。

障害への対応を決定する

アクターに障害が発生した場合、親は4つのディレクティブのいずれかを発行できます。

  • Resume:障害を無視して、アクターがメッセージの処理を続行します。
  • Restart:アクターを停止して再作成した後、処理を再開します。内部状態はリセットされます。
  • Stop:アクターを完全に終了します。
  • Escalate:障害を親のスーパーバイザー(祖父母アクター)に引き継ぎます。

OneForOne戦略

OneForOneStrategyは、よく使われるスーパービジョン戦略です。これは、ディレクティブが障害を起こした子アクターにのみ適用されることを意味します。

たとえば、1つの子アクターがクラッシュした場合、親はその子だけを再起動し、他の子アクターには影響を与えないようにできます。

OneForOneの動作

OneForOneStrategyを見てみましょう。ArithmeticExceptionが発生した場合は子を再起動し、それ以外の例外では停止する親を作成します。

import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

object OneForOneExample extends App {
  class Child extends Actor {
    override def preStart(): Unit = {
      println(s"Child ${self.path.name}: Started!")
    }
    override def postStop(): Unit = {
      println(s"Child ${self.path.name}: Stopped!")
    }
    override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
      println(s"Child ${self.path.name}: Pre-restart due to ${reason.getMessage}")
    }
    override def postRestart(reason: Throwable): Unit = {
      println(s"Child ${self.path.name}: Post-restart!")
    }
    def receive = {
      case "divideByZero" => throw new ArithmeticException("Oops, dividing by zero!")
      case "nullPointer"  => throw new NullPointerException("Something is null!")
      case msg            => println(s"Child ${self.path.name}: Received $msg")
    }
  }

  class Parent extends Actor {
    override val supervisorStrategy: SupervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) {
        case _: ArithmeticException => Restart
        case _: NullPointerException => Stop
        case _                      => Escalate
      }

    val child = context.actorOf(Props[Child], "myChild")

    def receive = {
      case msg => child ! msg
    }
  }

  val system = ActorSystem("OneForOneSystem")
  val parent = system.actorOf(Props[Parent], "myParent")

  parent ! "Hello"
  parent ! "divideByZero" // Should cause a restart
  Thread.sleep(100)
  parent ! "Hello again"
  Thread.sleep(1000) // Give time for restart to complete

  parent ! "nullPointer"  // Should cause a stop
  Thread.sleep(100)
  parent ! "Hello after null" // This message will not be processed by child
  Thread.sleep(2000)

  system.terminate()
}

OneForOneの動作を確認する

前のコードを実行しましたか。何が起きたかを確認しましょう。

  • "divideByZero"が送信されると、子がArithmeticExceptionをスローしました。親はRestartを適用し、「Pre-restart」と「Post-restart」のログが表示されました。
  • その後、子は「Hello again」を処理しました。
  • "nullPointer"が送信されると、子がNullPointerExceptionをスローしました。親はStopを適用し、子は終了しました。
  • 「Hello after null」も送信されましたが、子はすでに停止していたため処理できませんでした。

AllForOne戦略

場合によっては、1つの子の障害によって、すべての兄弟アクターも影響を受けたり、正しく機能できなくなったりすることがあります。

AllForOneStrategyは、障害を起こした子だけでなく、スーパーバイザーのすべての子に同じディレクティブを適用します。1つの子に障害が発生すると、選択されたディレクティブの影響がすべての子に及びます。

AllForOneの動作

親がAllForOneStrategyを使用するように変更してみましょう。1つの子の障害が兄弟アクターにどのような影響を与えるかに注目してください。

import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

object AllForOneExample extends App {
  class Child extends Actor {
    override def preStart(): Unit = {
      println(s"Child ${self.path.name}: Started!")
    }
    override def postStop(): Unit = {
      println(s"Child ${self.path.name}: Stopped!")
    }
    override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
      println(s"Child ${self.path.name}: Pre-restart due to ${reason.getMessage}")
    }
    override def postRestart(reason: Throwable): Unit = {
      println(s"Child ${self.path.name}: Post-restart!")
    }
    def receive = {
      case "fail" => throw new RuntimeException("Child failed!")
      case msg    => println(s"Child ${self.path.name}: Received $msg")
    }
  }

  class Parent extends Actor {
    override val supervisorStrategy: SupervisorStrategy =
      AllForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) {
        case _: RuntimeException => Restart
        case _                   => Escalate
      }

    val childA = context.actorOf(Props[Child], "childA")
    val childB = context.actorOf(Props[Child], "childB")

    def receive = {
      case "failA" => childA ! "fail"
      case "msgB"  => childB ! "Hello from B"
      case msg     => println(s"Parent received: $msg")
    }
  }

  val system = ActorSystem("AllForOneSystem")
  val parent = system.actorOf(Props[Parent], "myParent")

  parent ! "msgB" // Child B receives a message
  Thread.sleep(100)

  parent ! "failA" // Child A fails, causing all children to restart
  Thread.sleep(1000)

  parent ! "msgB" // Child B (restarted) receives another message
  Thread.sleep(2000)

  system.terminate()
}

判定ロジックをカスタマイズする

判定関数を使って、非常に柔軟なスーパービジョンロジックを作成できます。この関数はThrowable(例外)を受け取り、Directiveを返します。

これにより、例外の種類、原因となったメッセージ、さらにはスーパーバイザーの状態に基づいた複雑なルールを実装できます。

高度なスーパービジョン

スーパーバイザーの戦略内でカスタム判定関数を定義し、きめ細かな制御を行う方法を見てみましょう。

import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

object CustomDeciderExample extends App {
  class Worker extends Actor {
    def receive = {
      case "criticalError" => throw new IllegalStateException("Critical state!")
      case "minorError"    => throw new IllegalArgumentException("Bad input!")
      case msg             => println(s"Worker: Received $msg")
    }
  }

  class Supervisor extends Actor {
    override val supervisorStrategy: SupervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 10.seconds) {
        case _: IllegalArgumentException => Resume // Minor error, just resume
        case _: IllegalStateException    => Restart // Critical, restart
        case _: Exception                => Stop    // Unknown, stop
        case _                           => Escalate // Others, escalate
      }

    val worker = context.actorOf(Props[Worker], "myWorker")

    def receive = {
      case msg => worker ! msg
    }
  }

  val system = ActorSystem("CustomDeciderSystem")
  val supervisor = system.actorOf(Props[Supervisor], "mySupervisor")

  supervisor ! "Hello"
  supervisor ! "minorError" // Should Resume
  Thread.sleep(100)
  supervisor ! "Hello again" // Worker should still be active
  Thread.sleep(500)

  supervisor ! "criticalError" // Should Restart
  Thread.sleep(100)
  supervisor ! "After critical" // Worker should be restarted
  Thread.sleep(1500)

  system.terminate()
}

理解度を確認する

childAとchildBという2つの子アクターを監督する親アクターを考えてみましょう。親は、すべての例外に対してRestartディレクティブを適用するAllForOneStrategyを使用しています。

childAが例外をスローすると、何が起こるでしょうか。

振り返り:フォールトトレランス

よくできました。このレッスンでは、Akkaの強力なスーパービジョンの仕組みについて学びました。

  • スーパービジョン階層:親が子を監督します。
  • ディレクティブ:Resume、Restart、Stop、Escalate
  • OneForOneStrategy:障害が発生した子にのみディレクティブを適用します。
  • AllForOneStrategy:すべての子にディレクティブを適用します。
  • カスタム判定関数:障害処理をきめ細かく制御します。

これらのツールは、Akka Actorsで堅牢でレジリエントな並行アプリケーションを構築するために不可欠です。

よくある質問

「スーパービジョンと耐障害性」レッスンは無料ですか?

はい。「スーパービジョンと耐障害性」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全3レッスンが含まれています。

「スーパービジョンと耐障害性」で何を学びますか?

Akkaのスーパービジョン戦略を学び、障害から適切に復旧できる堅牢なシステムを構築します。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/3です。

「スーパービジョンと耐障害性」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Akkaアクターモデルの基礎
  2. アクターシステムの設計
  3. スーパービジョンと耐障害性
← Scala for Backend Engineering & Functional Programmingに戻る