Page History
Info |
---|
Akka에서의 분산처리 클러스터의 최종 목표는, 이미 작성된 외부 클러스터 시스템을 이용만 하는것이 아닌 단일지점 병목지점이 없는 확장가능한 시스템을 직접 구현하는것입니다. 외부 시스템을 그냥 사용하는것이 때로는 효율적일수 있지만, 직접 구현해보는것은 클러스터 시스템을 이용하는데 있어서 도움이 될것입니다. |
Table of Contents |
---|
클러스터 분산처리
클러스터는 여러 대의 컴퓨터들이 연결되어 하나의 시스템처럼 동작하는 컴퓨터들의 집합을 말하며
...
여러대의 컴퓨터를, 하나로 묶기위해 필요한 요소중 하나이며 아파치에서는 주키퍼와 유사한 역활을 합니다.이하 등대로 표현아파치에서 주키퍼와 그역활이 유사합니다.
Rancher(목장관리),주키퍼(동물원사육사)등 클러스터시스템들은
유일하게 위치 투명성있는 관리 시드 노드가 항상 존재하며,재미있는 네이밍이 붙어있습니다.
Akka.net에서는 이하 등대로 표현하겠습니다.
git : https://github.com/psmon/AkkaForNetCore/tree/master/LightHouse
클러스터 OverView
Gossip Protocol
단일지점 병목을 없애기위해 피어투 프로토콜을 이용하며, 각노드는 빠르게 특정대상(정확히는 구성된 라우터)에게 이야기할수 있으며
...
- 최초 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가 가입하거나 탈퇴할때에만 발생합니다.동일한 설정으로 설정변경없이 노드를 유연하게 확장할때 장점이있으며
피어투피어 네트워크는 다음과 같이 유연하며 다음과 같은 리얼 세계와 연관을 시키면 이해가 쉽습니다.
- 작업공간은 새로 참여할수도 있고, 중간에 나갈수도 있다.
- 새로운 담당자가 회의실에 참여하여역활의 담당자(노드)가 공간에 참여하면, 모든 직원과 구성원과 명함을 주고 받는다. (여기서 회의실은 등대역활)
- 담당 영역을 서로 확인했기때문에, 이후에는 중간 관리자(병목지점) 없이 빠른 실무처리
- 처리된 중간 결과를 중간 관리자가 알게 하기위해서는 싱글톤 클러스터 이용가능 ( 추후 설명)
- 모든 전문가는 각각의 역활을 알고있기때문에, 직접 대화가가능하며, 동일한 역활의 담당자가 추가되면 그룹구성을한다.
기존 설계된 로컬 액터에 클러스터 룰 셋팅만하고, 라우팅 전략역시 설정만으로 분산처리를 설계할수 있습니다.
...
Expand | ||||||
---|---|---|---|---|---|---|
| ||||||
|
클러스터 탑재하기
설정
Code Block |
---|
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 : 자신이 가져야할 롤 지정(복수개지정가능)
로컬액터 클러스터액터로 전환
Code Block |
---|
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 |
...
도메인 메시지를 처리하는 Received에는 코드변화가 없습니다.
클러스터로 구성된 액터에게 메시지 보내기
Code Block |
---|
// 클러스터 액터 설정 : https://github.com/psmon/AkkaForNetCore/blob/master/AkkaNetCore/Startup.cs services AkkaLoad.AddActor<ClusterMsgActorProvider>((provider, actorFactory) => { var actor = actorFactoryRegisterActor( "clusterRoundRobin", actorSystem.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 clusterMsgActorProviderclusterRoundbin1; public ActorTestController(ClusterMsgActorProvider _clusterMsgActorProvider) { clusterMsgActorProviderclusterRoundbin1 = _clusterMsgActorProvider(AkkaLoad.ActorSelect("clusterRoundRobin"); } [HttpPost("/cluster/msg/tell")] public void ClusterMsg(string value, int count) { for (int i = 0; i < count; i++) clusterMsgActorProviderclusterRoundbin1.Tell(value); } |
클러스터 한방에 로컬에서 뛰우기
클러스터 개발의 난제는, 로컬에 구성요소를 N개를 뛰우고 디버깅이 가능한가인데, 도커가 없을 시절
...
Code Block | ||||
---|---|---|---|---|
| ||||
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" |
측정가능
클러스터에서 발생하는 도메인 메시지에대한 모니터링및 측정은 장애대응/성능향상등을 위해 중요한요소이며
단순하게 CPU사용량/네트워크 사용량만을 이용하지 않습니다. 설계된 메시지에대한 유연한 오브젝트 단위 카운팅이가능하며
다양한 모니터링 툴과 연동 가능합니다. ( 이전장 참고 - 06. 실시간 메시지 성능 카운팅 )
여기서는, 3대를 클러스터로 묶은후 110만 메시지를 분산 전송하고 ,유실여부와 TPS를 측정하였습니다.
...
- 단일노드 : 1700tps
- 클러스터(3) : 5100tps
...
단일노드가 기대치 이하이거나(100Tps 이하) 자신이 만든 기능에 대해 성능 측정을 할수 없다고 가정해봅시다.
...
Code Block |
---|
[2020-02-29 12:20:53.1335] .....
====== Process Total:1000000 Seconds(100):0.0155927 Msg:sdsdsd
[2020-02-29 12:21:15.0558] .....
====== Process Total:1099900 Seconds(100):0.267527 Msg:sdsdsd
[2020-02-29 12:21:15.9675] .....
====== Process Total:1100000 Seconds(100):0.9117147 Msg:sdsdsd |
...
110만 메시지가 유실없이 전송됨을 검증 , 10만 처리(구간 1000000~110000)가
30초 이내에 완료됨을 자체 로깅시스템으로도 확인이 가능합니다.
클러스터 에서 발생하는 메시징의 총합계가 실시간 로깅을 통해 측정가능함을 의미하고
멀티 스레드 컴퓨팅환경에서 로컬컴퓨터가가진 메모리의 합계를 내는것도 꽤나 골치아픈일인데
네트워크로 분리된 메시징의 총합계를 실시간으로 측정하는 마법과같은 일을 할수 있을까요?
해답은 클러스터 싱글톤이라는 컨셉이있으며, 클러스터 내에서도 단 하나만 존재하여 여러 클러스터에서발생하는
내용을 중앙 집계처리할수가 있습니다. 단일지점이기때문에 메시징빈도를 주의해야할 필요는 있으나
이 샘플에서는 빈도수 최적화를 하지 않았으며, TPS 5000 정도는 무난하게 처리가능합니다.
git : https://github.com/psmon/AkkaForNetCore/blob/master/AkkaNetCore/Actors/Study/SingleToneActor.cs
데이터 분산처리
이 샘플에서는, 메시징을 별도로 저장하지 않았으며 처리중인 메시지는 영속화를 할수가 있습니다.
...
클러스터 싱글톤을 포함하여 샤딩처리및 영속화는 다음장에 다음장 이후에 다뤄보겠습니다.
Links :
- https://getakka.net/articles/clustering/cluster-overview.html
- https://getakka.net/articles/clustering/cluster-sharding.html
- https://getakka.net/articles/clustering/distributed-data.html
...