클러스터 시스템에서 중요한 특징 중 하나는 **위치 투명성(location transparency)**으로, 클러스터 내의 노드나 액터가 어디에 위치해 있는지 상관없이 동일한 방식으로 접근하고 사용할 수 있다는 점입니다.

이를 통해 클러스터 시스템의 확장성과 유연성을 크게 높일 수 있습니다.

클러스터 내에서 Point To Point 전송

클러스터 내에서는 가장 작은단위의 메시지 전송방식은 보낼대상 지점을 통해 P2P 전송이 가능합니다.

실제 중간지점의 전송계층을 경유하지 않고 클러스터내 Node내 액터간 1:1 통신을 할수 있으며 물리적인 위치를 알필요없이 위치투명성을 이용합니다.


클러스터 테스트 코드

// A와 B는 다른 포트로 구성된 Node입니다.
testKitA = ActorTestKit.create("ClusterSystem",cluster1)
testKitB = ActorTestKit.create("ClusterSystem",cluster2)

    @Test
    fun testClusterRouter(){
		//테스트를 위한 준비코드
        val probeA = testKitB.createTestProbe<Receptionist.Listing>()
        val probeB = testKitB.createTestProbe<HelloActorAResponse>()

        var list = testKitB.system().receptionist()
        list.tell(Receptionist.find(ClusterHelloActorA.ClusterHelloActorAKey, probeA.ref))
        val listing = probeA.receiveMessage() 
		

		//실제 활용되는 코드
        val router = listing.getServiceInstances(ClusterHelloActorA.ClusterHelloActorAKey)

        router.forEach {
            it.tell(HelloA("Hello", probeB.ref))
            probeB.expectMessage(HelloAResponse("Kotlin"))
        }
    }


클러스터 액터정의

/** HelloActor 클래스 */
class ClusterHelloActorA private constructor(
    private val context: ActorContext<HelloActorACommand>,
) : AbstractBehavior<HelloActorACommand>(context) {

    companion object {

        var ClusterHelloActorAKey:ServiceKey<HelloActorACommand> = ServiceKey.create(HelloActorACommand::class.java, "ClusterHelloActorA")

        fun create(): Behavior<HelloActorACommand> {
            return Behaviors.setup { context -> ClusterHelloActorA(context) }
        }
    }

    init {
        //Router
        context.system.receptionist().tell(Receptionist.register(ClusterHelloActorAKey, context.self))

    }


특정 Role이 부여된 액터배치

val clusterA = Cluster.get(testKitA.system())
// Role에 따라 작동하는 Actor 구분생성
if (clusterA.selfMember().hasRole("helloA")) {
	actorA = testKitA.spawn(ClusterHelloActorA.create(), "actorA")
}


가장 작은단위의 전송인 PointToPoint 전송을 기본으로 지원하게되면  Topic단위 또는 이벤트 버스를 구현할때도 최소 전송단위가 존재하기때문에  브로드캐스트를 효율적인 전송을 위해 활용해 수신처에서 필터기법을 사용할수도 있지만

필수적으로 필요로 하지는 않게됩니다.



Pub/Sub 시스템에서 상태없는 서비스와 상태있는 액터 모델을 사용했을 때의 메시지 전송 빈도 효율을 비교해 보겠습니다. 특히, 이벤트 생성 시마다 브로드캐스트가 필요하지 않도록 효율적 설계하는 방법도 다뤄보겠습니다.

1. 상태없는 서비스 기반 Pub/Sub

2. 상태있는 액터 모델 기반 Pub/Sub

브로드캐스트 지양을 위한 설계 방안

이벤트를 생성할 때마다 브로드캐스트하지 않도록 하는 몇 가지 방법을 상태있는 액터 모델과 함께 적용할 수 있습니다.

  1. 필터링과 조건 기반 전송:

  2. 이벤트 집계 (Aggregation):

  3. 스냅샷 기반 전송:

결론

따라서 이벤트 발생 빈도가 높거나 구독자가 많은 시스템에서는 상태있는 액터 모델을 사용하는 것이 효율적이며, 브로드캐스트 빈도를 줄여 네트워크와 시스템 리소스를 절약할 수 있습니다.


가정 및 설정 추가

이를 바탕으로 이벤트가 발생할 때마다 중간 장치에서 최종 사용자까지의 네트워크 전송 수를 다시 계산해 보겠습니다.

네트워크 이벤트 전송 수 계산

  1. 상태없는 Pub/Sub:

  2. 상태있는 액터 클러스터: 액터가 아니더라도 BroadCast를 사용하지 않고 중간 필터가능

  3. Akka 분산 Pub/Sub:

이를 바탕으로 단위별 비교 표를 작성하겠습니다.

이벤트 수상태없는 Pub/Sub 전송 수상태있는 액터 클러스터 전송 수Akka 분산 Pub/Sub 전송 수
110,0101,01051
10100,10010,100510
1001,001,000101,0005,100
1,00010,010,0001,010,00051,000
10,000100,100,00010,100,000510,000
100,0001,001,000,000101,000,0005,100,000

분석 및 결론

이와 같은 구성에서는 Akka 분산 Pub/Sub 방식이 가장 효율적이며, 특히 이벤트 발생 수가 많거나 네트워크 부하가 높은 환경에서 큰 절감 효과를 제공합니다.


다음장에서는 전송지점 어딘가에 브로드 캐스트가 항상 존재해야하는 전통적인 StateLess PubSub에서 상태기반의 분산처리 가능한 PubSub 을 알아보겠습니다.