여러 actor에게 여러 일들을 위임하거나 분산시킬 때, Router
를 사용하면 유리하다. 라우터는 다른 액터에게 메시지를 forward
하는 중간레벨의 actor이다. 라우터는 바깥에서 생성되거나 그들 자신들로부터 생성된다.
우리는 라우터 풀을 만들어 다른 여러 actor에게 여러 메시지를 위임시키고 싶다. 이렇다면 어떻게 해야할까? 라우터 로직은 라운드 로빈이고, 일을 위임 받은 액터는 5개이며 타입은 Slave라고 하면 코드는 다음과 같을 것이다.
val poolMaster = system.actorOf(RoundRobinPool(5).props(Props[Slave]), "slavePoolMaster")
system.actorOf
까지는 일반적인 ActorRef를 선언하는 것과 유사하지만, 내부 파라미터에서 갈린다. Props
가 들어가야 할 곳에 단순히 Props만 선언되는 것이 아닌, 라우터로직Pool(n)
과 같은 형태로 작성한다. 그리고 그 뒤에 props
를 붙여 어떤 액터에 대한 것인지 명시한다.
위의 방법은 코드 내부에서 직접 설정해주는 것이고, Config에서 가져오는 방법도 있다. application.conf
파일 내부에서 다음과 같이 코드 블럭을 넣는다.
routersDemo {
akka {
actor.deployment {
/poolMaster2 {
router = round-robin-pool
nr-of-instances = 5
}
}
}
}
맨 바깥 블럭에는 라우터 config 명을 짓는다. router에 관한 예시였으므로 routersDemo
라고 지었다. 그리고, akka, actor.deployment 블럭 내부에 인스턴스화할 액터 라우터명을 입력하고, router logic과 인스턴스 개수를 선언한다.
그리고 다시 ActorSystem
이 선언되어 있는 오브젝트로 넘어와서 다음과 같이 작성한다.
val system = ActorSystem("routerSystem", ConfigFactory.load().getConfig("routersDemo"))
val poolMaster2 = system.actorOf(FromConfig.props(Props[Slave]), "poolMaster2")
위와 같이 작성하면 된다. 즉, ActorSystem
을 선언할 때 ConfigFactory.load().getConfig(불러온 Config명)
을 통해 config에 선언되어 있는 내용을 불러오고, system.actorOf
내에서 FromConfig
를 통해 config를 가져와서 인스턴스화를 진행하면 된다.
pool이 아니라 group으로 선언하는 방식이 있다. 하지만, 이 역시 pool로 선언하는 방식과 유사하다.
val slaveList = (1 to 5).map(idx => system.actorOf(Props[Slave], s"slave_$idx")).toList
val slavePaths = slaveList.map(slave => slave.path.toString)
val groupMaster = system.actorOf(RoundRobinGroup(slavePaths).props())
와 같이 코드를 작성한다. groupMaster
value만 본다면 pool을 선언하는 방식과 굉장히 유사하지만, ActorRef
들의 path를 받아온다는 점을 유의하면 된다.
configuration 역시 위의 Pool 방식과 유사한 점이 많다. 가장 먼저 application.conf
에 들어간다.
routersDemo {
akka{
actor.deployment {
/groupMaster2 {
router = round-robin-group
routees.paths = ["/user/slave_1", "/user/slave_2", "/user/slave_3", "/user/slave_4", "/user/slave_5"]
}
}
}
}
위와 같이 작성한다. 여기서 Pool
방식과의 차이점이라면 pool
방식은 인스턴스의 수를 입력하면 되었지만, 1번에서 보았듯, Path들을 작성해주어야 한다.
그리고 ActorSystem이 선언되어 있는 오브젝트로 넘어와서 다음과 같이 작성한다.
val system = ActorSystem("routerSystem", ConfigFactory.load().getConfig("routersDemo"))
val groupMaster2 = system.actorOf(FromConfig.props(), "groupMaster2")
와 같이 선언하면 된다.
거의 사용되지 않는 방법이지만, 필기해두려고 한다.
class Master extends Actor{
// step 1 - create routees
private val slaves = (0 until 5).map(index => {
val slave = context.actorOf(Props[Slave],s"slave$index")
context.watch(slave)
ActorRefRoutee(slave)
})
// step2 - define router
private val router = Router(RoundRobinRoutingLogic(), slaves) // router logic,
override def receive: Receive = {
// step 4 - handle the termination/lifecycle of the routees
case Terminated(ref) =>
router.removeRoutee(ref)
val newSlave = context.actorOf(Props[Slave])
context.watch(newSlave)
router.addRoutee(newSlave)
// step 3 - route the messages
case message =>
router.route(message, sender())
}
}
slave
가 들어있는 sequence를 만든다. 이때, Routee
타입을 가져야 하므로 ActorRefRoutee(actorRef명)
을 통해 타입을 변환한다.
그리고 Router(라우팅로직, slave)
를 통해 라우터를 정의하면 된다.
그리고, router.route(메시지, 발신자)
를 통해 라우팅하면 된다.