Page History
...
Akka는 기본적으로 탑레벨 아키텍쳐의 설계방식으로 Actor를 위치시키고 접근을 하며 (최상위부터 생성하여 구조화하는 방식 )
다음과같은 특성이 있습니다.
- 액터접근 : 트리구조이지만, FullName을 알고있을시 최하위 자식노드에게 직접 리모트 메시지전송이
...
- 가능합니다.
- 액터접근 : someActor의 자식에게 모두 메시지를 보내고 싶으면 someActor/* 를 할수 있습니다.
- 라우팅화 가능 : 분산처리를 위해 자식노드에게 다양한 라우팅을 지정할수 있습니다. 순차적으로 분산처리 해야한다고하면 라운드 로빈으로 자식 액터 배치가 가능합니다.
- 리모트 액터 : 모든게 메시징를 통해서만 명령이 전달되며 로컬과 리모트의 차이가 없습니다.
- 클러스터 : 로컬과 리모트의 차이가 없다란것은 로컬로 개발된 액터가 약간의 코드수정으로 단일지점 병목없는 클러스터화 가능합니다.
성능 전략에따라 다양한 라우트(ex>라운드로빈)를 선택할수있고 기존 서비스코드의 변경이 거의 없다. ( 로컬객체의 확장 유연성)
또한 클러스터 룰정의를 통해 코드의 수정및 서비스 Down없이 스케일아웃 배포에대한 문제해결도 가능합니다. ( 유연한 스케일아웃)
장애 처리 관점에서 자식의 예외는 부모가 처리하는것을 1원칙으로, 부모의 예외를 자식노드에게 전가시키 지 않습니다.
이로써 상위 감독자에게 장애에대한 룰을 각각 적용할수가 있습니다. ( 장애를 허용하고 최상위 감독자를 통한 장애대책 )
접근이 용이하다란 말은 Actor가 어디에 있던 메시지를 전송시킬수 있다란 의미이지, 객체 자체를 접근할수 있다란 의미는 아닙니다.( 객체 공유는 액터모델에 위배됨 )
Actor의 기본 생성과 Child Actor 생성
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
public class PrintMyActorRefActor extends ActorMyActor : ReceiveActor { override defprivate receive:ILoggingAdapter Receivelog = Context.GetLogger(); public MyActor() { case "printit" Receive<string>(message => { val secondRef = context.actorOf(Props.empty, "second-actor") //자식의 생성에 대한 책임은 부모가 가진다. ( 생성하고 자식노드로 런타임 이동하는 형태의 방법 제한) if (message == "createChild") { Context.ActorOf<MyActor>("myChild"); printlnSender.Tell(s"Second: $secondRefCreate Child Succed:myChild"); } } val firstRef = system.actorOf(Props[PrintMyActorRefActor], "first-actor") // actorOf 는 Actor생성시 사용됩니다. println(s"First : $firstRef") firstRef ! "printit" // !는 스칼라에서 Input을 위한 키워드이며, c++의 cout >> 과 유사한 키워드 , //C#또는 JAVA에서는 명시적으로 firstRef.Tell("printit") 이렇게 사용하면 됩니다. //액터 생성 순서 first-actor <= second-actor 가 parent <= child 관계가 된다. First : Actor[akka://testSystem/user/first-actor#1053618476] Second: Actor[akka://testSystem/user/first-actor/second-actor#-1544706041] |
아래와같이, rest의 endpoint 와 접근법이 유사합니다.
어떠한 의미에서 RESAPI 설계도 Top-Level 아키텍쳐를 따른다고 볼수 있습니다. ( 상위에서 하위기능으로 분류)
}
else
{
Sender.Tell("RE:" + message);
}
});
Receive<SomeMessage>(message => {
Sender.Tell("RE:" + message.message);
});
}
}
IActorRef myActor = actorSystem.ActorOf<MyActor>("myactor");
myActor.tell("createChild");
|
액터접근
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
Code Block | ||||||
| ||||||
var parentActor= system.ActorSelection("akka://testSystem/user/first-actormyactor"); var childActor= system.ActorSelection("akka://testSystem/user/first-actor/second-actormyactor/myChild"); |
RestAPI에서는 호출을 한번하면 내부적으로 ( 접속-요청-응답-접속해제) 의 과정을 거치지만
원격 Actor의 경우 이미 연결된 객체(TCP피어투피어) 를 사용하며 ,새로운 연결에대한 오버헤드가 없기때문에
대량의 호출시, 호출시마다 HTTP의 무지막지한 헤더값이 없을뿐더러
CPU/메모리/네트워크자원 을 훨씬 작게 사용합니다. ( 성능적으로 약 100배 이상 차이 )
사용자에게 제공하는 ResAPI를 대체하는 의미는 아니며, 미들웨어간 정보를 주고받을때 RestAPI를 사용하고 있다면 고성능 정보교환 문제를 해결할수 있습니다.
...
title | 패킷설계에대한 오래된 이슈 |
---|
bool SomeOptionA;
bool SomeOptionB;
int SomeNumOption = 3; //range 1-100000
10년전 네트워크 환경을 고려하면 위와같은 패킷 정의는 바람직 하지 않았습니다.
작은 단위의 데이터를 사용하는것은 DB뿐아니라 네트워크 전송환경에서도 고려해야하는 사항으로.
위 구조체는 6byte를 차지하며, 아래와 같이 3byte로 줄이는게 가능합니다.
컴파일러 최적 에따라 4바이트 경계사용시 실제는 8 / 4바이트가 됩니다.
이것을 줄이기위해 pragma pack이라는 다소 부자연스러운 키워드도 사용하게 됩니다.
unsigned char SomeOptions; //OR연산 비트플래그 처리로 작은 데이터에 다중옵션가능합니다.
unsigned short SomeNumOption; //음수영역을 제거함으로, 기존 사용하던 데이터범위보다 한단계 작은 단위 사용가능
하지만 네트워크 성능이 충분히 좋은 요즘은, 의미해석 관점에서 이러한 설계가 오히려 바람직하지 않는 케이스가 되었습니다.
비트 연산한 값을 DB에 저장해버리면, 어떠한 의미에서는 매직넘버가 될수가 있습니다.
또한 최소의 값을 사용하여 확장발생시 새로운 필드를 추가해야 하는케이스보다 적절하게 너무크기않은 충분한 값을 사용하여 데이터 범위 확장에 대비하는것이
훨씬더 이득이기 때문입니다.
잘설계된 RestAPI의 endpoint 와 접근법이 유사합니다.
어떠한 의미에서 REST-API 설계도 Top-Level 아키텍쳐를 따른다고 볼수 있습니다. ( 상위에서 하위기능으로 분류)
작동방식에서의 차이는 REST-API는 무상태를 지향하지만, 액터의 경우 상태가 있는 서비스 설계에 더 유리합니다.
액터성능
로컬에서만 사용될때는 메모리를 사용하며 , 리모트 전송이 발생할때는 고성능 TCP프로토콜을 활용합니다.
로컬에서는 주로 멀티스레드를 관리하는 Dispatcher에 의해 액터의 성능이 결정될수 있으며
리모트에서는 고성능 TCP모듈및 , 원격컴퓨터간 데이터복원을 위한 직렬화에 영향을 받을수 있으며
컴포넌트 형태로 이것은 모두 코드변경없이 가장 빠른모듈로 손쉽게 교체가 가능합니다.
액터의 소멸과 생명주기
구조적인
( 필드의 명확성 웹확장성및 이기종 통신을 고려하여 요즘은 json(serializer) 선택의 문제로 축소가됨 )
위와같은 개발경험으로 처음 웹서비스를 서버를 통해 제공하는 코드를 작성 했을시 HttpRequest가 500byte이상의 헤더값이 항상 존재하는것과
500byte면 1분정도의 게임 한사이클을 처리할수 있는 사이즈이며, Http 전송횟수제한과 더불어 네트워크 성능 튜닝을 위한 걸림돌이였습니다.
액터의 소멸
이런 구조적인 설계로, 부모의 액터를 정지시키면 자식의 액터를 모두 종류후 마지막에 부모 액터가 종료가됩니다.
상속을 받은 OOP Class의 소멸 순서와도 유사합니다.
이것은 부모가 자식을 책임지는 AKKA의 기본 예외 처리모델과도 부합하며, OOP방식의 소멸순서와도 유사합니다.관리감독할수 있으며, 장애복구전략에 따라 자식액터를 재생성 할수 있습니다.
다만 부모에게 발생한 예외를 자식에게 전가시키지 않습니다. ( 예외처리모델에 더 상세하게 설명)
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
public class StartStopActor1 extends Actor { override def preStart(): Unit =: ReceiveActor { private ILoggingAdapter log = Context.GetLogger(); public StartStopActor1() { Receive<string>(message => { if (message == "stop") { Context.Stop(Self); } }); } protected override void PreStart() { println log.Info("first started"); context.actorOf(Props[StartStopActor2], Context.ActorOf<StartStopActor2>("second"); } protected override defvoid postStopPostStop(): Unit = println { log.Info("first stopped"); } } public overrideclass defStartStopActor2 receive: ReceiveReceiveActor = { case "stop" private ILoggingAdapter log => context.stop(self) } } class StartStopActor2 extends Actor { override def preStart(): Unit = printlnContext.GetLogger(); public StartStopActor2() { Receive<string>(message => { }); } protected override void PreStart() { log.Info("second started"); override def postStop(): Unit = println("second stopped") } // Actor.emptyBehavior is a useful placeholder when we don't // want to handle any messages in the actor. override def receive: Receive = Actor.emptyBehavior } val first = system.actorOf(Props[StartStopActor1], "first") first ! "stop" protected override void PostStop() { log.Info("second stopped"); } } IActorRef myActor = actorSystem.ActorOf<StartStopActor1 >("myactor"); myActor.Tell("stop"); //OutPut //first started //second started //second stopped //first stopped |
...
Supervisor을 통한
...
장애허용시스템
Child 액트가 액터가 장애발생시, 디양한 다양한 복구전략( 3초간 30초동안시도)을 통해 Child액트의 Child액터의 복구가 가능하다가능합니다.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
var supervisor = BackoffSupervisor.Props( Backoff.OnFailure( childProps, childName: "supervised-actor", minBackoff: TimeSpan.FromSeconds(3), maxBackoff: TimeSpan.FromSeconds(30), randomFactor: 0.2) ); |
이 섹션은 아래와 같은 실습코드를 통해 좀더 살펴보겠습니다.
...
Include Page | ||||
---|---|---|---|---|
|