Supervisão e tolerância a falhas
Explore as estratégias de supervisão do Akka para criar sistemas resilientes, capazes de se recuperar de falhas de forma adequada.
Supervisão e tolerância a falhas é uma aula grátis de Scala for Backend Engineering & Functional Programming no CoddyKit. Esta é a aula 3 de 3. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Scala for Backend Engineering & Functional Programming, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Scala for Backend Engineering & Functional Programming inclui 3 aulas no total.
Criando sistemas resilientes
Em aplicações concorrentes, as coisas podem dar errado. Um ator pode falhar, uma chamada de rede pode falhar ou um banco de dados pode ficar inacessível.
Tolerância a falhas é a capacidade de um sistema continuar funcionando corretamente mesmo quando partes dele falham. O Akka fornece mecanismos poderosos para alcançar isso.
Supervisão entre pai e filho
Os atores do Akka são organizados em uma hierarquia, muito semelhante a uma árvore genealógica. Quando um ator cria outro ator, ele se torna seu pai.
- Os pais são responsáveis por supervisionar seus filhos.
- Se um ator filho falhar, seu pai receberá uma notificação.
- O pai então decide como lidar com a falha do filho. Isso é chamado de estratégia de supervisão.
Decidindo o que fazer em caso de falha
Quando um ator falha, seu pai pode emitir uma de quatro diretivas:
Resume: o ator continua processando mensagens, ignorando a falha.Restart: o ator é interrompido, recriado e então retoma o processamento. Seu estado interno é redefinido.Stop: o ator é terminado permanentemente.Escalate: a falha é encaminhada ao supervisor do pai (seu avô).
Estratégia OneForOne
A OneForOneStrategy é uma estratégia de supervisão comum. Isso significa que uma diretiva se aplica apenas ao ator filho que falhou.
Por exemplo, se um ator filho falhar, o pai poderá decidir reiniciar somente esse filho específico, sem afetar os demais.
OneForOne em ação
Vamos ver a OneForOneStrategy. Criaremos um pai que reinicia um filho diante de uma ArithmeticException, mas o interrompe diante de outras exceções.
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()
}Observando o OneForOne
Você executou o código anterior? Veja o que aconteceu:
- Quando
"divideByZero"foi enviado, o filho lançou umaArithmeticException. O pai aplicouRestart. Você viu os registros "Pre-restart" e "Post-restart". - Em seguida, o filho processou "Hello again".
- Quando
"nullPointer"foi enviado, o filho lançou umaNullPointerException. O pai aplicouStop. O filho foi terminado. - "Hello after null" foi enviado, mas o filho já havia sido interrompido e não pôde processá-lo.
Estratégia AllForOne
Às vezes, uma falha em um filho significa que todos os filhos irmãos também podem estar comprometidos ou incapazes de funcionar corretamente.
A AllForOneStrategy aplica a mesma diretiva a todos os filhos do supervisor, não apenas àquele que falhou. Se um filho falhar, todos os filhos serão afetados pela diretiva escolhida.
AllForOne em ação
Vamos modificar nosso pai para usar a AllForOneStrategy. Observe como a falha de um filho afeta seu irmão.
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()
}Personalizando os decisores
Você pode criar uma lógica de supervisão altamente personalizada usando uma função decisora. Essa função recebe um Throwable (a exceção) e retorna uma Directive.
Isso permite implementar regras complexas com base no tipo de exceção, na mensagem que a causou ou até mesmo no estado do supervisor.
Supervisão avançada
Veja como definir um decisor personalizado na estratégia do supervisor para obter um controle detalhado.
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()
}Verifique seus conhecimentos
Considere um ator pai supervisionando dois atores filhos, childA e childB. O pai usa uma AllForOneStrategy com a diretiva Restart para todas as exceções.
Se childA lançar uma exceção, o que acontecerá?
Recapitulação: tolerância a falhas
Excelente trabalho! Nesta lição, você aprendeu sobre os poderosos mecanismos de supervisão do Akka:
- Hierarquia de supervisão: os pais supervisionam os filhos.
- Diretivas:
Resume,Restart,Stop,Escalate. OneForOneStrategy: aplica diretivas somente ao filho que falhou.AllForOneStrategy: aplica diretivas a todos os filhos.- Decisores personalizados: oferecem controle detalhado sobre o tratamento de falhas.
Essas ferramentas são essenciais para criar aplicações concorrentes robustas e resilientes com atores do Akka!
Aprenda Scala com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 39
- Aulas
- 143
Perguntas Frequentes
A aula “Supervisão e tolerância a falhas” é grátis?
Sim — o texto completo de “Supervisão e tolerância a falhas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Scala for Backend Engineering & Functional Programming, atualize para CoddyKit PRO. O curso de Scala for Backend Engineering & Functional Programming inclui 3 aulas no total.
O que vou aprender em “Supervisão e tolerância a falhas”?
Explore as estratégias de supervisão do Akka para criar sistemas resilientes, capazes de se recuperar de falhas de forma adequada. Você pratica Scala for Backend Engineering & Functional Programming com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Scala for Backend Engineering & Functional Programming?
Nenhuma experiência prévia é necessária. Scala for Backend Engineering & Functional Programming no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 3.
Quanto tempo leva a aula “Supervisão e tolerância a falhas”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Scala for Backend Engineering & Functional Programming?
Sim. Cada aula de Scala for Backend Engineering & Functional Programming inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Fundamentos do modelo de atores do Akka
- Projetando sistemas de atores
- Supervisão e tolerância a falhas