You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 15 Next »

Akka에서의 분산처리 클러스터의 최종 목표는, 이미 작성된 클러스터 시스템을 이용만 하는것이아닌

분산처리를 위한 클러스터를 직접 설계하고 구현하여  단일지점 병목점을 해결하여 유연하게

확장가능한 시스템을 구현하는것입니다.

클러스터 분산처리

클러스터는 여러 대의 컴퓨터들이 연결되어 하나의 시스템처럼 동작하는 컴퓨터들의 집합을 말하며

LightHouse(등대)는 클러스터내에 서버 발견(Discovery)과 가입/탈퇴를 관리하는 역활을 하며,

여러대의 컴퓨터를, 하나로 묶기위해 필요한 요소중 하나이며 아파치에서는 주키퍼와 유사한 역활을 합니다.

이하 등대로 표현


git : https://github.com/psmon/AkkaForNetCore/tree/master/LightHouse

클러스터 OverView

Gossip Protocol

단일지점 병목을 없애기위해 피어투 프로토콜을 이용하며, 각노드는 빠르게 특정대상(정확히는 구성된 라우터)에게 이야기할수 있으며

설정없이 서로의 존재를 알기위해 잡답(Gossip) 방식을 사용합니다.

  • 최초 Node1을 뛰운다.
    • A1 : Node1은 인스턴스를 뛰우고 난후, 등대 에게 자신의 존재를 알린다.
  • Node2를 뛰운다
    • A2 : 노드2는 인스턴스를 뛰우고 난후, 등대에게 자신의 존재를 알린다.
    • A1 : 등대는 Node1에게 Node2의 존재를 알린다.
    • B1 : Node1은 Node2에게 인사를 하여 서로 연결을 맺어둔다.
    • A2 : 연결이 완료되었음을 등대에게 알린다.
  • Node3를 뛰운다.
    • 동일과정 : A3 → (A1,A2) → (B3,B2) → A3
  • Node1이 탈퇴(Down)한다.
    • A1 : Node1이 등대에게 탈퇴를 알린다. ( 비정상 종료시에는 등대가 Node1이 사라짐을 파악후 제거진행)
    • A3,A2 : 등대가 Node1의 탈퇴 사실을 알린다.
    • B3,B1 : Node1과의 연결을 제거한다.

위 작동방법이 복잡하고 트래픽을 증가시키는것처럼 보이지만, 신규 Node가 가입하거나 탈퇴할때에만 발생합니다.

동일한 설정으로 노드를 유연하게 확장할때 유연하며 다음과 같은 리얼 세계와 연관을 시키면 이해가 쉽습니다.

  • 새로운 담당자가 회의실에 참여하여, 모든 직원과 명함을 주고 받는다. (여기서 회의실은 등대역활)
  • 담당 영역을 서로 확인했기때문에, 이후에는 중간 관리자(병목지점) 없이 빠른 실무처리
  • 처리된 중간 결과를 중간 관리자가 알게 하기위해서는 싱글톤 클러스터 이용가능 ( 추후 설명)


기존 설계된 로컬 액터에 클러스터 룰 셋팅만하고, 라우팅 전략역시 설정만으로 분산처리를 설계할수 있습니다.

라운드 로빈만이 유일한 라우팅으로 알고 있다고 하면, AKKA에서는 다음과 같은 라우팅을 클러스터에서 사용할수 있습니다.

분산 처리의 세계에서는 구현된 로컬 로직 변경을 최소화하고 다양한 라우팅을 사용할수 있습니다.

Unable to render {include} The included page could not be found.


클러스터 탑재하기

설정

akka {
	remote {
		log-remote-lifecycle-events = debug
        dot-netty.tcp {
            port = 7000
			hostname = 127.0.0.1
		}
    }

	actor {
		provider = cluster
		deployment {
			/cluster-roundrobin {
				router = round-robin-pool # routing strategy
				#routees.paths = ["/user/clustermsg"]
				nr-of-instances = 500 # max number of total routees
				cluster {
					enabled = on
					allow-local-routees = on
					use-role = akkanet
					max-nr-of-instances-per-node = 20
				}
			}
		}
	}

	cluster {
        seed-nodes = ["akka.tcp://actor-cluster@127.0.0.1:7100"] # address of seed node
        roles = ["akkanet"] # roles this member is in
        auto-down-unreachable-after = 300s
		debug {
			verbose-heartbeat-logging = off
			verbose-receive-gossip-logging = off
		}
    }
}

주요설정

  • actor.privider = cluster : 해당 액터들을 클러스터로 사용하겠다.
  • cluster-roundrobin.cluster.user-role = 롤이름 : 라운드 로빈방식으로, 롤 이름으로 지정된 노드에서 작동됨
  • cluster.seed-nodes : 등대역활 지정
  • cluster.roles : 자신이 가져야할 롤 지정(복수개지정가능)

로컬액터 클러스터액터로 전환

protected Cluster Cluster = Akka.Cluster.Cluster.Get(Context.System);
private bool ClusterMode = true;

protected override void PreStart()
{
	// subscribe to IMemberEvent and UnreachableMember events
	if (ClusterMode)
	{
		Cluster.Subscribe(Self, ClusterEvent.InitialStateAsEvents,
		new[] { typeof(ClusterEvent.IMemberEvent), typeof(ClusterEvent.UnreachableMember) });
	}
}

protected override void PostStop()
{
	if (ClusterMode) Cluster.Unsubscribe(Self);	
}
// src : https://github.com/psmon/AkkaForNetCore/blob/master/AkkaNetCore/Actors/ClusterMsgActor.cs

기존 로컬전용 액터에, 잡담(Gossip) 기능을 추가하는것으로 클러스터 기능 작동준비가 완료됩니다.

도메인 메시지를 처리하는 Received에는 코드변화가 없습니다.


클러스터로 구성된 액터에게 메시지 보내기

// 클러스터 액터 설정 : https://github.com/psmon/AkkaForNetCore/blob/master/AkkaNetCore/Startup.cs
services.AddActor<ClusterMsgActorProvider>((provider, actorFactory) =>
{
	var actor = actorFactory.ActorOf(Props.Create<ClusterMsgActor>(0)
		.WithDispatcher("fast-dispatcher")
		.WithRouter(FromConfig.Instance), "cluster-roundrobin");
	return () => actor;
});
			

// 클러스터 액터 DI 참조 얻기및 메시지 보내기  : https://github.com/psmon/AkkaForNetCore/blob/master/AkkaNetCore/Controllers/ActorTestController.cs
			
private readonly IActorRef clusterMsgActorProvider;
public ActorTestController(ClusterMsgActorProvider _clusterMsgActorProvider)
{            
	clusterMsgActorProvider = _clusterMsgActorProvider();
}

[HttpPost("/cluster/msg/tell")]
public void ClusterMsg(string value, int count)
{
	for (int i = 0; i < count; i++)
		clusterMsgActorProvider.Tell(value);
}

클러스터 한방에 로컬에서 뛰우기

클러스터 개발의 난제는, 로컬에 구성요소를 N개를 뛰우고 디버깅이 가능한가인데, 도커가 없을 시절

멀티노드를 뛰우는것은 상당히 불편한 과정에 하나였습니다. 

요즘 대부분의 IDE들은 Docker(+Compose,+쿠버네틱)개발환경을 지원하고 있음으로

운영과 비슷한 조건의 노드 구성을 하고 구동시킬수 있으며 디버깅지원은 보너스입니다.

샘플에서는 추가로 DataDog(https://www.datadoghq.com/) 매트릭스 연동하였습니다. ( 클러스터에 발생하는 메시지에대한 모니터링)

version: '3.4'

services:
  datadog:
    image: datadog/agent:7
    ports:
      - 8125/udp
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /proc/:/host/proc/:ro
      - /sys/fs/cgroup/:/host/sys/fs/cgroup:ro
    environment:
      DD_API_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      DD_DOGSTATSD_NON_LOCAL_TRAFFIC: "true"

  lighthouse:
    image: ${DOCKER_REGISTRY-}lighthouse
    build:
      context: .
      dockerfile: LightHouse/Dockerfile
    environment:
      CLUSTER_IP: lighthouse
      CLUSTER_PORT: 4053
      CLUSTER_SEEDS: akka.tcp://actor-cluster@lighthouse:4053

  akkanetcore:
    image: ${DOCKER_REGISTRY-}akkanetcore
    build:
      context: .
      dockerfile: AkkaNetCore/Dockerfile
    depends_on:
     - lighthouse
    ports:
     - 8080:5000
    environment:
      ASPNETCORE_ENVIRONMENT: Development
      MonitorTool: datadog
      MonitorToolCon: datadog
      port: 5000
      akkaport: 7100
      akkaip: akkanetcore
      roles: "akkanet"
      akkaseed: "akka.tcp://actor-cluster@lighthouse:4053"

  akkanetcore2:
    image: ${DOCKER_REGISTRY-}akkanetcore
    build:
      context: .
      dockerfile: AkkaNetCore/Dockerfile
    depends_on:
     - lighthouse    
    environment:
      ASPNETCORE_ENVIRONMENT: Development
      MonitorTool: datadog
      MonitorToolCon: datadog
      port: 5000
      akkaport: 7000
      akkaip: akkanetcore2
      roles: "akkanet"
      akkaseed: "akka.tcp://actor-cluster@lighthouse:4053"

  akkanetcore3:
    image: ${DOCKER_REGISTRY-}akkanetcore
    build:
      context: .
      dockerfile: AkkaNetCore/Dockerfile
    depends_on:
     - lighthouse    
    environment:
      ASPNETCORE_ENVIRONMENT: Development
      MonitorTool: datadog
      MonitorToolCon: datadog
      port: 5000
      akkaport: 7000
      akkaip: akkanetcore3
      roles: "akkanet"
      akkaseed: "akka.tcp://actor-cluster@lighthouse:4053"

데이터 분산처리

DC가 단일 DB라고 가정해봅시다. AKKA에는 메시지를 저장하는 영속성 기능도 지원하지만

중요 도메인 데이터에대해 RDB로 구성된것을 AKKA의 Persist 로 전환하는것은 바람직하지 않습니다.

RDB는 가장 안정적이고 검증이된 Persist역활을 하는 것중 하나이며 ,RDB의 부담을 덜어주는 방향으로

대용량 이벤트 데이터를 분산처리 하여 저장한후 RDB와 연동하는것이 추천됩니다. (이 후장에대 다룰예정)


Links : 


  • No labels