[Akka] Actor Lifecycle

smlee·2023년 9월 6일
0

Akka

목록 보기
15/50
post-thumbnail

Akka Framework에서 사용하는 스칼라의 Actor는 JVM 상에서 분산적이며 동시성이 있는 프로그램들을 작성하는데 도움을 준다. 이러한 액터들은 독립적이지만, 상당히 가볍다. 200만 개의 액터들이 힙에서 차지하는 메모리 영역은 고작 1GB이다. 이러한 액터들은 생성되어지고, 재시작되거나 멈춰질 수 있다. 이러한 액터들의 생명 주기에 대해 알아보려고 한다.

Actor Lifecycle

위에서 언급했듯 액터들은 생성될 수 있으며, 재시작 역시 가능하며 중지될 수 있다. 이때 알아야 할 것은 언제 그리고 어떻게 이러한 작용들이 일어나는지 이해하는 것이 중요하다.

작용들을 이해하여 응용하면 프로그램의 생명주기에서 이득을 볼 수 있다. 예를 들어 액터가 시작하기 전, DB를 연결하거나 정보들을 로깅할 수 있다. 또한 액터가 멈추기 직전 리소스들을 릴리즈할 수 있다. 그리고 재시작 전에 이전의 상태를 간직할 수 있다.

위의 이미지는 액터의 생명주기를 도식화한 이미지이다.

액터의 생명 주기는 actorOf()라는 메서드가 contextsystem으로부터 호출되었을 때부터 시작된다. 이렇게 actorOf()를 실행하면 인스턴스가 생성된다.

(1) preStart()

preStart()라는 메서드는 인스턴스가 생성되고 실행되면 액터가 시작된다. 이때, 이 preStart() 메서드를 오버라이드하여 액터의 자세한 implementation을 설정할 수 있다. 주로 액터가 실행되기 전 DB 커넥션 설정이나, 파일 설정 등을 위해 사용된다.

import akka.actor.{Actor, ActorSystem, Props}

class PreStart extends Actor {
	override def receive:Receive = {
    	case name => println("Hello " + name)
    }
    
    override def preStart():Unit = {
    	println("preStart() method called...")
    }
}

object Application extends App {
	val system = ActorSystem("ActorLifeCycleDemo")
    val actor = system.actorOf(Props[PreStart], "actor")
    actor ! "knoldus"
}

위의 코드는 이름을 받으면 Hello 다음 이름을 출력한다. 만약 위의 코드가 실행되면

위와 같이 출력된다. 즉, 액터가 생성 후 preStart가 되어 실행되게 된다.

postStop()

그리고 위의 생명주기를 보면 stop() 메서드 후 액터가 멈추지만, postStop()이라는 메서드가 실행이 되어야 액터가 종료된다. 이 postStop() 메서드 역시 preStart() 메서드처럼 구체적인 implementation을 할 수 있다.

만약 이 메서드가 호출되면 액터는 새 메시지를 받는 것을 중단하고, 현재 메시지는 dead letter mailbox로 리다이렉트된다.

import akka.actor.{Actor, ActorSystem, Props}

class PostStop extends Actor {
	override def receive: Receive = {
    	case name => println("Hello " + name)
    }
    
    override def postStop():Unit = {
    	println("postStop() method called...")
    }
}

object Application extends App {
	val system = ActorSystem("ActorLifeCycleDemo")
    val actor = system.actorOf(Props[PostStop], "actor")
    
    actor ! "knoldus"
    
    system.stop(actor)
}

위는 postStop을 사용한 예시이다. 이 앱을 실행하면 다음과 같은 결과를 얻을 수 있다.


receive 메서드에서 메시지를 받은 후 결과값이 출력되고 액터가 멈춘 후 종료되기 전 해당 메서드가 실행된다.

(3) preRestart()

preRestart 역시 재시작되기 전에 호출되는 메서드이다.

import akka.actor.{Actor, ActorSystem, Props}

class PreRestart extends Actor {
	override def receive: Receive = {
    	case number:Int => number / 0
        case name => println("Hello " + name)
    }
    
    override def preRestart(reason:Throwable, message: Option[Any]):Unit = {
    	println("preRestart() method called...")
        println("Reason : " + reason)
    }
}

object Application extends App {
	val system = ActorSystem("ActorlifeCycleDemo")
    val actor = system.actorOf(Props[PreRestart], "actor")
	actor ! 20
}

위의 코드를 실행하면 어떻게 될까? 숫자가 메시지로 오면 0으로 나눌 것이다. 즉, 오류가 발생하여 액터는 Fault 상태가 될 것이고 멈출 것이다.

위와 같이 재시작 전에 preRestart가 실행하여 오류 메시지가 출력되는 것을 알 수 있다.

(4) postRestart()

postRestart 역시 구체적으로 implementation할 수 있는 메서드로, 액터가 crash 이후 다시 초기화할 때 사용된다.

import akka.actor.{Actor, ActorSystem, Props}

class PreRestart extends Actor {
	override def receive: Receive = {
    	case number:Int => number / 0
        case name => println("Hello " + name)
    }
    
    override def postRestart(reason:Throwable):Unit = {
    	println("postRestart() method called...")
        println("Reason : " + reason)
    }
}

object Application extends App {
	val system = ActorSystem("ActorlifeCycleDemo")
    val actor = system.actorOf(Props[PreRestart], "actor")
	actor ! 20
}

위의 코드를 실행하면 이렇게 실행이 된다.

Reference

0개의 댓글