Play Framework - 원격 Akka 액터에 연결하기
원본 : http://qiita.com/visualskyrim/items/350ba0112cd9a95388ff
참고:
디펜던시 추가
build.sbt 에 Akka 원격 라이브러리를 추가한다.
libraryDependencies ++= Seq(
// ... other libs
"com.typesafe.akka" %% "akka-remote" % "2.3.4"
)
Akka 설정 조정
For API
Play API 를 위해서 conf/application.conf 에 있는 디폴트 Akka 세팅을 다음과 같이 변경 한다.
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider" # offer the provider
}
remote {
enabled-transports = ["akka.remote.netty.tcp"] # enable protocol
netty.tcp {
hostname = "127.0.0.1" # your host
port = 2553 # port
}
}
}
만약 설정 파일에 아카 오브젝트가 없다면, 설정파일에 그냥 추가하라.
For Actor
위에서 했던 것과 비슷한 설정을src/main/resources/application.conf에 하라.
yourSystem { # the name your actor system is going to use
akka {
# other thing is just the same as that in API
loglevel = "DEBUG"
loggers = ["akka.event.slf4j.Slf4jLogger"]
actor {
provider = "akka.remote.RemoteActorRefProvider"
default-dispatcher {
}
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2552 # Note if you are running API and Actor program in localhost, make sure they are not using the same port
}
}
}
}
그리고나서 코드에 이 세팅을 적용하라.
// ... some codes for launch the program
val system = ActorSystem("CoolSystem", config.getConfig("yourSystem"))
Note 위의 provider 를 해당 아카 버전으로 주의 있게 바꾸고 올바른 프로바이더를 선택하라.
Program
API
자바를 이용해서 플레이를 사용할 것이다. 스칼라에 대한 것은 여기를 봐라. this.
컨트롤러안에서 리모트 액터로 연결한다. 아래 코드 참고 :
public static F.Promise<Result> askYourActorSomething(final String info) {
String actorPath = actorHelper.getPath(); // get akka path of your worker, this will not show in my example
ActorSelection actor = Akka.system().actorSelection(actorPath);
return play.libs.F.Promise.wrap(ask(actor, new MessageToActor(info), 5000)).map( // use ask pattern so that we can get sync response from actor; wrap into Promise
new F.Function<Object, Result>() { // the callback when actor sends back response
public Result apply(Object resp) {
return ok((String) resp);
}
}
);
}
여기서 주의깊게 봐야할 포인트는 만약 ask 패턴을 사용한다면 결과를 Promise 로 감싸야한다는점.
Actor
액터 코드:
class Worker extends Actor {
override def receive = {
case MessageToActor(info) => // get message from API
sender ! "worked!" // response to API
}
}
런치 코드
// fetch configs
val remoteConfig = ConfigFactory.load.config.getConfig("yourSystem").getConfig("akka").getConfig("remote").getConfig("netty.tcp")
val actorHost = remoteConfig.getString("hostname")
val actorPort = remoteConfig.getInt("port")
val workerName = "worker"
val actorPath = "akka.tcp://" + "yourSystem" + "@" + actorHost + ":" + actorPort + "/user/" + workerName
println(actorPath) // here you know what your actor's path is, well, just for show, don't do this sort of thing your code.
val system = ActorSystem("CoolSystem",config.getConfig("yourSystem"))
val actor = system.actorOf((new Worker()), name = workerName)
이제 만약 컨트롤러 askYourActorSomething 가 호출되면, 경로에 해당하는 당신의 액터에 메세지를 보낼 것이다. 그리고 나서 액터는 이 메세지를 받고 API 컨트롤러에게 다시 문자를 돌려 줄 것이다. 마지막으로 API 는 "worked!" 를 리턴 할 것 이다.
한 가지 더 ..
제품화된 플레이 어플리케이션에서 원격 액터를 사용할 예정이라면 , 특별히 분산 환경이라면 , 좀 더 해야할 것들이 생긴다..
방화벽
API 와 액터 프로그램이 서로 접근하지 못하게 할 것이다.
만약 EC2 를 사용한다면 security groups. 를 세팅해서 해결하라. 서로 다른 그룹의 인바운드에 있는지 확인해야한다.