Play Framework를 쓰는 이유 중 하나는 Akka Actor를 사용하기 위해서일 것이다. 물론 Spring에서도 쓸 수는 있지만 어플리케이션 생명 주기를 따르는 내부적인 Actor System을 기본적으로 가지고 있다는 것은 큰 장점이다. 덕분에, Akka Actor Depengency를 따로 추가할 필요도 없다.
Play Framework에서 Actor를 만드는 방법은 크게 두 가지가 있다. ActorSystem을 주입받아 액터를 만들고 사용하는 방법과 AkkaGuiceSupport를 구현한 모듈을 통해 싱글톤 액터를 만드는 방법이다.
예제를 위해 간단한 액터 클래스를 정의해 두자.
import akka.actor._
case object PING
case object PONG
class PingPongActor extends Actor{
override def receive: Receive = {
case PING => sender ! PONG
case PONG => sender ! PING
}
}
PING을 받으면 PONG을, PONG을 받으면 PING object를 돌려주는 액터.
1. ActorSystem을 주입받아 액터 만들기
ActorSystem을 주입받아 사용한다는 점 외에는 특별할 것이 없다.
import akka.actor._
import akka.pattern._
import akka.util.Timeout
import scala.concurrent.duration._
@Singleton
class SimpleController @Inject()
(actorSystem: ActorSystem) extends Controller{
implicit val timeout = Timeout(2 seconds)
val pingPongActor: ActorRef = actorSystem.actorOf(Props[PingPongActor])
def index = Action { implicit request =>
val result = Await.result(pingPongActor ? PING, Duration.Inf)
Ok(result.toString)
}
}
2. AkkaGuiceSupport를 상속한 모듈로 싱글톤 액터 만들기
주입가능한 싱글톤 액터를 만들기 위해서는 먼저 AbstractModule과 AkkaGuiceSupport를 상속받은 모듈을 만든다.
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class ActorConfigModule extends AbstractModule with AkkaGuiceSupport{
override def configure(): Unit = {
bindActor[PingPongActor]("pingpongActor")
// 파라미터로 받은 문자열은 Named 어노테이션으로 구분할 수 있게 해준다.
}
}
modules.ActorConfgModule.scala
conf/application.conf 파일에 만든 모듈을 추가한다.
play.modules.enabled += modules.ActorConfigModule
모듈에서 바인드한 싱글톤 액터를 주입받아 사용한다.
@Singleton
class SimpleController @Inject()
(@Named("pingpongActor") actor: ActorRef) extends Controller{
implicit val timeout = Timeout(2 seconds)
def index = Action { implicit request =>
val result = Await.result(actor ? PONG, Duration.Inf)
Ok(result.toString)
}
}
3) 액터에 주입하기
액터에 @Inject 어노테이션을 이용해 객체를 주입할 수도 있다.
import javax.inject.Inject
import akka.actor._
import models.Something
class SomethingActor @Inject()
(s: Something) extends Actor{
override def receive: Receive = {
case _ => sender ! s.message
}
}
import akka.actor._
import akka.pattern._
import akka.util.Timeout
import scala.concurrent.duration._
@Singleton
class SimpleController @Inject()
(@Named("somethingActor") s: ActorRef) extends Controller{
implicit val timeout = Timeout(2 seconds)
def index = Action { implicit request =>
val result = Await.result(s ? "Hello", Duration.Inf)
Ok(result.toString)
}
}
4) Scheduling
ActorSystem을 주입 받아 주기적, 혹은 특정 시간에 액터에 메시지를 전달하는 Scheduling이 가능하다.
// @Inject()(actorSystem: ActorSystem)
import scala.concurrent.ExecutionContext.Implicits._
// (시작 딜레이, 간격, ActorRef, 보낼 메시지)
actorSystem.scheduler.schedule(0 seconds, 300 seconds, pingpongActor, PING)
// (시작 딜레이, ActorRef, 보낼 메시지)
actorSystem.scheduler.scheduleOnce(0 seconds, pingpongActor, PONG)
// (시작 딜레이)(Unit을 반환하는 함수)
actorSystem.scheduler.scheduleOnce(0 seconds){
println("Hello!")
}
그 외에 액터를 동적으로 만들어 주입하는 액터 팩토리나 액터의 쓰레드 개수같은 상세한 설정은 공식 문서에서 볼 수 있다.
'Scala > Play framework' 카테고리의 다른 글
Play Framework 2.5. Built-in Filters, CSRF Filter, Custom Filter (0) | 2017.04.16 |
---|---|
Play Framework 2.5. Action, Security Object (로그인/보안) (0) | 2017.04.14 |
Play Framework 2.5. Singleton과 Inject (0) | 2017.04.13 |
Play Framework 2.5 + Play Slick 2.1.0. 외래 키, 조인 (0) | 2017.04.12 |
Play Framework 2.5 + Play Slick 2.1.0. 연동, 기본적인 쿼리 (0) | 2017.04.12 |