Scala för backendutveckling och funktionell programmering · Lektion

Övervakning och feltålighet

Utforska Akka:s övervakningsstrategier för att skapa motståndskraftiga system som kan återhämta sig smidigt efter fel.

Lektion 3 av 312 steg

Övervakning och feltålighet är en gratis lektion i Scala för backendutveckling och funktionell programmering på CoddyKit. Detta är lektion 3 av 3. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Scala för backendutveckling och funktionell programmering, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Scala för backendutveckling och funktionell programmering innehåller totalt 3 lektioner.

Bygga motståndskraftiga system

I samtidiga applikationer kan saker gå fel. En aktör kan krascha, ett nätverksanrop kan misslyckas eller en databas kan vara otillgänglig.

Feltolerans är ett systems förmåga att fortsätta fungera korrekt även när delar av det slutar fungera. Akka tillhandahåller kraftfulla mekanismer för att uppnå detta.

Övervakning mellan överordnade och underordnade aktörer

Akka-aktörer är organiserade i en hierarki, ungefär som ett släktträd. När en aktör skapar en annan aktör blir den dess överordnade aktör.

  • Överordnade aktörer ansvarar för att övervaka sina underordnade aktörer.
  • Om en underordnad aktör misslyckas får den överordnade aktören en avisering.
  • Den överordnade aktören avgör sedan hur felet hos den underordnade aktören ska hanteras. Detta kallas en övervakningsstrategi.

Besluta hur fel ska hanteras

När en aktör misslyckas kan den överordnade aktören utfärda ett av fyra direktiv:

  • Resume: Aktören fortsätter att behandla meddelanden och ignorerar felet.
  • Restart: Aktören stoppas, skapas på nytt och fortsätter sedan att behandla meddelanden. Dess interna tillstånd återställs.
  • Stop: Aktören avslutas permanent.
  • Escalate: Felet skickas vidare till den överordnade aktörens övervakare (dess morförälder).

OneForOne-strategin

OneForOneStrategy är en vanlig övervakningsstrategi. Det innebär att ett direktiv endast tillämpas på den underordnade aktör som misslyckades.

Om exempelvis en underordnad aktör kraschar kan den överordnade aktören välja att endast starta om just den aktören, medan de andra underordnade aktörerna förblir opåverkade.

OneForOne i praktiken

Låt oss titta på OneForOneStrategy. Vi skapar en överordnad aktör som startar om en underordnad aktör vid ett ArithmeticException, men stoppar den vid andra undantag.

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()
}

Observera OneForOne

Körde Ni den föregående koden? Detta var vad som hände:

  • När "divideByZero" skickades kastade den underordnade aktören ett ArithmeticException. Den överordnade aktören tillämpade Restart. Ni såg loggarna "Pre-restart" och "Post-restart".
  • Den underordnade aktören behandlade sedan "Hello again".
  • När "nullPointer" skickades kastade den underordnade aktören ett NullPointerException. Den överordnade aktören tillämpade Stop. Den underordnade aktören avslutades.
  • "Hello after null" skickades, men den underordnade aktören var redan stoppad och kunde inte behandla det.

AllForOne-strategin

Ibland innebär ett fel hos en underordnad aktör att alla syskonaktörer också kan vara påverkade eller oförmögna att fungera korrekt.

AllForOneStrategy tillämpar samma direktiv på alla övervakarens underordnade aktörer, inte bara på den som misslyckades. Om en underordnad aktör misslyckas påverkas alla underordnade aktörer av det valda direktivet.

AllForOne i praktiken

Låt oss ändra vår överordnade aktör så att den använder AllForOneStrategy. Lägg märke till hur ett fel hos en underordnad aktör påverkar dess syskonaktör.

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()
}

Anpassa beslutsfunktioner

Ni kan skapa mycket anpassad övervakningslogik med hjälp av en beslutsfunktion. Funktionen tar emot en Throwable (undantaget) och returnerar ett Directive.

Detta gör det möjligt att implementera komplexa regler baserade på typen av undantag, meddelandet som orsakade det eller till och med övervakarens tillstånd.

Avancerad övervakning

Så här definierar Ni en anpassad beslutsfunktion i Er övervakningsstrategi för detaljerad kontroll.

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()
}

Kontrollera Era kunskaper

Betrakta en överordnad aktör som övervakar två underordnade aktörer, childA och childB. Den överordnade aktören använder en AllForOneStrategy med direktivet Restart för alla undantag.

Vad händer om childA kastar ett undantag?

Sammanfattning: feltolerans

Bra jobbat! I den här lektionen har Ni lärt Er om Akkas kraftfulla övervakningsmekanismer:

  • Övervakningshierarki: Överordnade aktörer övervakar underordnade aktörer.
  • Direktiv: Resume, Restart, Stop, Escalate.
  • OneForOneStrategy: Tillämpas endast på den underordnade aktör som misslyckades.
  • AllForOneStrategy: Tillämpas på alla underordnade aktörer.
  • Anpassade beslutsfunktioner: För detaljerad kontroll över felhanteringen.

Dessa verktyg är avgörande för att bygga robusta och motståndskraftiga samtidiga applikationer med Akka Actors!

Gratis att börja

Lär dig Scala med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
39
Lektioner
143

Vanliga frågor

Är lektionen ”Övervakning och feltålighet” gratis?

Ja – hela texten till ”Övervakning och feltålighet” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Scala för backendutveckling och funktionell programmering, kan Ni uppgradera till CoddyKit PRO. Kursen i Scala för backendutveckling och funktionell programmering innehåller totalt 3 lektioner.

Vad lär jag mig i ”Övervakning och feltålighet”?

Utforska Akka:s övervakningsstrategier för att skapa motståndskraftiga system som kan återhämta sig smidigt efter fel. Ni övar på Scala för backendutveckling och funktionell programmering med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Scala för backendutveckling och funktionell programmering?

Du behöver inga förkunskaper. Utbildningen i Scala för backendutveckling och funktionell programmering på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 3.

Hur lång tid tar lektionen ”Övervakning och feltålighet”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Scala för backendutveckling och funktionell programmering-lektionen?

Ja. Varje Scala för backendutveckling och funktionell programmering-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Grunderna i Akka Actor Model
  2. Design av aktörssystem
  3. Övervakning och feltålighet
← Tillbaka till Scala för backendutveckling och funktionell programmering