액터는 메시지 분산처리를 위해 분배기를 탑재해 하위에 구성할수 있습니다.
분배를 위한 라우터의 구성집합은 다음 두가지를 활용할수 있습니다.
![]()
Akka는 다양한 종류의 라우터를 제공하여 여러 액터에 메시지를 분산시킬 수 있도록 합니다. Akka의 라우터 종류는 다음과 같습니다:
RoundRobinRouter:
RandomRouter:
SmallestMailboxRouter:
BroadcastRouter:
ScatterGatherFirstCompletedRouter:
ConsistentHashingRouter:
TailChoppingRouter:
이러한 라우터를 사용하면 여러 액터로 작업을 분산하고, 시스템 성능을 최적화하며, 부하를 균등하게 관리할 수 있습니다. Akka는 각 라우터가 상황에 맞게 사용될 수 있도록 다양한 옵션을 제공합니다.
앞장에서 작성한 HelloActor의 분배처리를 위한 상위 액터를 생성시도해보겠습니다.
HelloActor를 관리하는 HelloManagerActor를 생성하고싶습니다. - HelloActor를 5개 Pool로 생성구성 |
package actor.router
import actor.Hello
import actor.HelloActor
import actor.HelloActorCommand
import actor.HelloActorResponse
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.SupervisorStrategy
import akka.actor.typed.javadsl.AbstractBehavior
import akka.actor.typed.javadsl.ActorContext
import akka.actor.typed.javadsl.Behaviors
import akka.actor.typed.javadsl.Receive
import akka.actor.typed.javadsl.PoolRouter
import akka.actor.typed.javadsl.Routers
sealed class HelloManagerCommand
data class DistributedHelloMessage(val message: String, val replyTo: ActorRef<HelloActorResponse>) : HelloManagerCommand()
class HelloManagerActor private constructor(
context: ActorContext<HelloManagerCommand>,
private var router: PoolRouter<HelloActorCommand>,
private val routerRef: ActorRef<HelloActorCommand>
) : AbstractBehavior<HelloManagerCommand>(context) {
companion object {
fun create(): Behavior<HelloManagerCommand> {
return Behaviors.setup { context ->
val router = Routers.pool(5, Behaviors.supervise(HelloActor.create())
.onFailure(SupervisorStrategy.restart()))
.withRoundRobinRouting()
val routerRef = context.spawn(router, "hello-actor-pool")
HelloManagerActor(context, router, routerRef)
}
}
}
override fun createReceive(): Receive<HelloManagerCommand> {
return newReceiveBuilder()
.onMessage(DistributedHelloMessage::class.java, this::onSendHelloMessage)
.build()
}
private fun onSendHelloMessage(command: DistributedHelloMessage): Behavior<HelloManagerCommand> {
routerRef.tell(Hello(command.message, command.replyTo))
return this
}
} |
중요 코드
package actor.router
import actor.HelloActorResponse
import actor.HelloResponse
import akka.actor.testkit.typed.javadsl.ActorTestKit
import akka.actor.testkit.typed.javadsl.TestProbe
import akka.actor.typed.ActorRef
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
class HelloManagerActorTest {
companion object {
private val testKit = ActorTestKit.create()
@JvmStatic
@BeforeAll
fun setup() {
// Setup code if needed
}
@JvmStatic
@AfterAll
fun tearDown() {
testKit.shutdownTestKit()
}
}
@Test
fun testSendHelloMessage() {
val helloManager: ActorRef<HelloManagerCommand> = testKit.spawn(HelloManagerActor.create())
val probe = TestProbe.create<HelloActorResponse>(testKit.system())
helloManager.tell(DistributedHelloMessage("Hello", probe.ref()))
helloManager.tell(DistributedHelloMessage("Hello", probe.ref()))
helloManager.tell(DistributedHelloMessage("Hello", probe.ref()))
helloManager.tell(DistributedHelloMessage("Hello", probe.ref()))
helloManager.tell(DistributedHelloMessage("Hello", probe.ref()))
probe.expectMessage(HelloResponse("Kotlin"))
probe.expectMessage(HelloResponse("Kotlin"))
probe.expectMessage(HelloResponse("Kotlin"))
probe.expectMessage(HelloResponse("Kotlin"))
probe.expectMessage(HelloResponse("Kotlin"))
}
} |
[akka://HelloManagerActorTest/user/$a/hello-actor-pool/$d] Received valid Hello message. Count incremented to 1 [akka://HelloManagerActorTest/user/$a/hello-actor-pool/$c] Received valid Hello message. Count incremented to 1 [akka://HelloManagerActorTest/user/$a/hello-actor-pool/$a] Received valid Hello message. Count incremented to 1 [akka://HelloManagerActorTest/user/$a/hello-actor-pool/$e] Received valid Hello message. Count incremented to 1 [akka://HelloManagerActorTest/user/$a/hello-actor-pool/$b] Received valid Hello message. Count incremented to 1 INFO akka.actor.CoordinatedShutdown - Running CoordinatedShutdown with reason [ActorSystemTerminateReason] > Task :test |
context.self.path()를 통해 액터에 접근가능한 주소를 알수 있으며~ 동일한 액터가 아닌 다른 액터에게 분산처리가 되는지 확인할수 있습니다.
액터모델을 이용해 다양한 문제를 해결할수 있는, AKKA에서 제공하는 다양한 메시지 패턴 툴들을 알아보았습니다.
액터모델은 동일언어를 사용하는 클러스터내에서 상태관리할수 있는 장점이 있지만, 브라우저와 같이 최종 엔드사용자를 위해 제공되는 프로토콜은 아닙니다.
다음장에서는 웹소켓 핸들러와 액터모델을 연결해 최종 사용자의 세션정보를 제어하고 실시간 이벤트를 전달하는 방법을 알아보겠습니다.