액터는 메시지 분산처리를 위해 분배기를 탑재해 하위에 구성할수 있습니다.


분배를 위한 라우터의 구성집합은 다음 두가지를 활용할수 있습니다.



Akka는 다양한 종류의 라우터를 제공하여 여러 액터에 메시지를 분산시킬 수 있도록 합니다. Akka의 라우터 종류는 다음과 같습니다:

  1. RoundRobinRouter:

  2. RandomRouter:

  3. SmallestMailboxRouter:

  4. BroadcastRouter:

  5. ScatterGatherFirstCompletedRouter:

  6. ConsistentHashingRouter:

  7. TailChoppingRouter:

이러한 라우터를 사용하면 여러 액터로 작업을 분산하고, 시스템 성능을 최적화하며, 부하를 균등하게 관리할 수 있습니다. Akka는 각 라우터가 상황에 맞게 사용될 수 있도록 다양한 옵션을 제공합니다.



앞장에서 작성한 HelloActor의 분배처리를 위한 상위 액터를 생성시도해보겠습니다.

HelloActor를 관리하는 HelloManagerActor를 생성하고싶습니다.
다음 규칙을 준수, 이 기반으로 액터모델을 작성해

- HelloActor를  5개 Pool로 생성구성
- 라운드 로빈(순차분배) 방식으로 명령을 내리고 싶습니다.


HelloManagerActor

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
    }
}

중요 코드

HelloManagerActorTest

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

    }
}


TEST결과

[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

액터모델을 이용해 다양한 문제를 해결할수 있는, AKKA에서 제공하는 다양한 메시지 패턴  툴들을 알아보았습니다.

액터모델은 동일언어를 사용하는 클러스터내에서 상태관리할수 있는 장점이 있지만, 브라우저와 같이 최종 엔드사용자를 위해 제공되는 프로토콜은 아닙니다.

다음장에서는 웹소켓 핸들러와 액터모델을 연결해 최종 사용자의 세션정보를 제어하고 실시간 이벤트를 전달하는 방법을 알아보겠습니다.

Next