[Akka] Routers

smlee·2023년 10월 12일
0

Akka

목록 보기
32/50
post-thumbnail

여러 actor에게 여러 일들을 위임하거나 분산시킬 때, Router를 사용하면 유리하다. 라우터는 다른 액터에게 메시지를 forward하는 중간레벨의 actor이다. 라우터는 바깥에서 생성되거나 그들 자신들로부터 생성된다.

1. pool routes

(1) programmatically in code

우리는 라우터 풀을 만들어 다른 여러 actor에게 여러 메시지를 위임시키고 싶다. 이렇다면 어떻게 해야할까? 라우터 로직은 라운드 로빈이고, 일을 위임 받은 액터는 5개이며 타입은 Slave라고 하면 코드는 다음과 같을 것이다.

val poolMaster = system.actorOf(RoundRobinPool(5).props(Props[Slave]), "slavePoolMaster")

system.actorOf까지는 일반적인 ActorRef를 선언하는 것과 유사하지만, 내부 파라미터에서 갈린다. Props가 들어가야 할 곳에 단순히 Props만 선언되는 것이 아닌, 라우터로직Pool(n)과 같은 형태로 작성한다. 그리고 그 뒤에 props를 붙여 어떤 액터에 대한 것인지 명시한다.

(2) Configuration

위의 방법은 코드 내부에서 직접 설정해주는 것이고, 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를 가져와서 인스턴스화를 진행하면 된다.

group routes

(1) programmatically in code

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를 받아온다는 점을 유의하면 된다.

(2) Configuration

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")

와 같이 선언하면 된다.

3. Manual Router

거의 사용되지 않는 방법이지만, 필기해두려고 한다.

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(메시지, 발신자)를 통해 라우팅하면 된다.

0개의 댓글