[Akka] Classic Cluster Singleton

smlee·2023년 9월 18일
0

Akka

목록 보기
26/50
post-thumbnail

클러스터 싱글톤 패턴은 akka.cluster.singleton.ClusterSingletonManager에 의해 구현된다. 모든 클러스터 노드 또는 특정 역할이 태그된 노드 그룹 중에서 하나의 싱글톤 액터 인스턴스를 관리한다. ClusterSingletonManager는 클러스터 내의 모든 노드 또는 지정된 역할이 있는 모든 노드에서 가능한 빨리 시작되어야 하는 액터이다. 실제 싱글톤 액터는 가장 오래된 노드에서 ClusterSingletonManager에 의해 supply된 Props에서 하위 액터를 생성하여 시작된다. ClusterSingletonManager는 최대 하나의 싱글톤 인스턴스가 임의의 시점에서 실행되도록 한다.

제공된 akkka.cluster.singleton.ClusterSingletonProxy를 사용하여 싱글톤 액터에 액세스할 수 있으며, 이는 모든 메시지를 싱글톤의 현재 인스턴스로 라우팅할 것이다. 프록시는 클러스터에서 가장 오래된 노드를 추적하고 싱글톤의 actorSelectionakkka.actor.Identify 메시지를 명시적으로 전송하여 싱글톤의 ActorRef를 해결한다. 이는 싱글톤이 특정(구성 가능한) 시간 내에 응답하지 않으면 주기적으로 수행된다. 구현이 주어졌을 때, 예를 들어 노드가 클러스터를 떠날 때 ActorRef를 사용할 수 없는 기간이 있을 수 있다. 이러한 경우, 프록시는 싱글톤으로 전송된 메시지를 버퍼링한 다음 싱글톤이 최종적으로 사용 가능할 때 전달할 것이다. 버퍼가 가득 차면 ClusterSingletonProxy는 프록시를 통해 새로운 메시지가 전송될 때 이전 메시지를 드롭할 것이다. 버퍼의 크기는 구성 가능하며 0의 버퍼 크기를 사용하여 비활성화할 수 있다.

예시

우리가 외부 시스템으로 들어가는 하나의 진입점이 필요하다고 가정하자. JMS 소비자가 단 한 명이어야만 메시지가 순서대로 처리된다는 엄격한 요건을 갖춘 JMS 큐의 메시지를 받는 행위자, 그것은 아마도 사물을 설계하는 방식이 아니라 외부 시스템과 통합할 때 전형적인 실제 시나리오이다.

클러스터 싱글톤 액터를 만드는 방법을 설명하기 전에 싱글톤에서 사용할 메시지 클래스를 정의해 보겠다.

object PointToPointChannel {
  case object UnregistrationOk extends CborSerializable
}
object Consumer {
  case object End extends CborSerializable
  case object GetCurrent extends CborSerializable
  case object Ping extends CborSerializable
  case object Pong extends CborSerializable
}

클러스터의 각 노드에서 ClusterSingletonManager를 시작하고 싱글톤 액터(이 경우 JMS 큐 대기열 소비자)의 Props를 제공해야 한다. 밑은 ClusterStingletonManagerProps로 시작하는 경우이다.

system.actorOf(
  ClusterSingletonManager.props(
    singletonProps = Props(classOf[Consumer], queue, testActor),
    terminationMessage = End,
    settings = ClusterSingletonManagerSettings(system).withRole("worker")),
  name = "consumer")

여기서는 싱글톤을 "worker" 역할이 태그된 노드로 제한하지만, 역할에 관계없이 모든 노드를 Role로 지정하지 않음으로써 사용할 수 있다.

실제로 싱글톤 Actor를 멈추기 전에 리소스를 닫을 수 있도록 애플리케이션 별 terminationMessage를 사용한다. PoisonPill은 actir를 멈추기만 하면 되는 완벽한 terminationMessage임에 유의해야 한다.

이 예제에서 싱글톤 액터가 종료 메시지를 처리하는 방법은 다음과 같다.

case End =>
  queue ! UnregisterConsumer
case UnregistrationOk =>
  stoppedBeforeUnregistration = false
  context.stop(self)
case Ping =>
  sender() ! Pong

위에 주어진 이름들로, 적절하게 구성된 프록시를 사용하여 어떤 클러스터 노드에서도 싱글톤에 대한 접근을 얻을 수 있다.

val proxy = system.actorOf(
  ClusterSingletonProxy.props(
    singletonManagerPath = "/user/consumer",
    settings = ClusterSingletonProxySettings(system).withRole("worker")),
  name = "consumerProxy")

Supervision

잠재적으로 감독 대상이 될 수 있는 Actor는 두 가지이다. 위에서 생성된 소비자 Singleton은 다음과 같다:

  • 클러스터의 모든 노드에서 실행되는 클러스터 싱글톤 관리자(예: /user/consumer)
  • 관리자가 가장 오래된 노드에서 시작하는 /user/consumer/singleton과 같은 사용자 actor

클러스터 싱글톤 매니저 액터는 항상 실행 중이어야 하기 때문에 감독 전략이 변경되어서는 안 된다. 그러나 때때로 사용자 액터에 대한 감독을 추가하는 것이 유용하다. 이를 달성하기 위해 '진짜' 싱글톤 인스턴스를 생성하는 데 사용될 상위 감독 액터를 추가한다. 아래는 구현 예이다.

import akka.actor.{ Actor, Props, SupervisorStrategy }
class SupervisorActor(childProps: Props, override val supervisorStrategy: SupervisorStrategy) extends Actor {
  val child = context.actorOf(childProps, "supervised-child")

  def receive = {
    case msg => child.forward(msg)
  }
}

그리고 위의 액터는 아래의 예시처럼 사용할 수 있다.

import akka.actor.{ PoisonPill, Props }
import akka.cluster.singleton.{ ClusterSingletonManager, ClusterSingletonManagerSettings }
context.system.actorOf(
  ClusterSingletonManager.props(
    singletonProps = Props(classOf[SupervisorActor], props, supervisorStrategy),
    terminationMessage = PoisonPill,
    settings = ClusterSingletonManagerSettings(context.system)),
  name = name)

Reference

0개의 댓글