Page History
Info |
---|
결함을 허용하지 않는 시스템은, 역설적으로 결함에 대응을 하지 않겠다란 의미입니다. ActorSystem 은 결함을 허용함으로(결함을 발생시키겠다란 의미 아님) 장애발생시 감시할수있는 장치를 제공하고 , 사용자가 직접 대처할수 있는 다양한 전략을 선택하거나 응용프로그램에 설계할수가 있습니다. |
장애 대응 설계
...
액터 장애처리모델은 자기자신이 아닌 자신의 부모를 통해 정의가 가능하며
어떠한 액터의 장애를 다음과 같이 설계하고 룰을 정의한다고 합시다.
기존 예외처리모델과 비교하여, 서비스코드와 예외상황을 완전하게
분리하여 작성 가능합니다. 즉 서비스 코드는 열심히 시도하면 되고, 장애처리에대한 플랜은
부모가 지는구조로 그것을 직접 설계를 하면됩니다OOP 예외처리 모델에서 자식이 부모를 책임질수있는 구조적 정의는 불가능합니다.
다음과 같이, 장애대응 플랜을 세웠다고 가정해봅시다.
- 장애 발생시 1분이내에 10번만 시도할수 있다. (복구 플랜)
- 액터 작동시 널참조로인한 예외는 재시작하여 복구한다.
- 액터 작동시 인자값 익셉션 에러일시, 해당 액터를 Stop시킨다.
Test Plan 은 아래와같습니다.
- 테스트에 사용할 액터를 생성한다. 부모 : supervisor , 자식 : child1
- child1액터에게 널 예외 유발시킨다.
- child1액터 crash 및 복구확인
- child1에게 메시지보내어 작동중인지 확인
- child1액터에게 인자값 예외 유발시킨다.
- child1가 crash가 된것 확인
- child1에게 메시지를 보내 사살확인
...
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
public class Supervisor : UntypedActor { private readonly ILoggingAdapter log = Context.GetLogger(); //장애처리 룰을 정의합니다. (서비스 코드와 완전분리) protected override SupervisorStrategy SupervisorStrategy() { return new OneForOneStrategy( maxNrOfRetries: 10, //최고 시도횟수 withinTimeRange: TimeSpan.FromMinutes(1), //최고 시도 시간 localOnlyDecider: ex => { // Exception별 복구전략 if( ex is ArithmeticException ) return Directive.Resume; else if( ex is NullReferenceException) return Directive.Restart; else if (ex is ArgumentException) return Directive.Stop; else return Directive.Escalate; }); } protected override void OnReceive(object message) { if (message is string) { string msg = message as string; if ( msg.Contains("create-") ) { string childName = msg.Split('-')[1]; log.Info("CreateChildActor:" + childName); var childActor = Context.ActorOf<Child>(childName); Context.Watch(childActor); Sender.Tell(childActor); } } } } public class Child : UntypedActor { private int state = 0; private readonly ILoggingAdapter log = Context.GetLogger(); protected override void OnReceive(object message) { if( message is Exception) //가상의 예외가 발생했다고 가정하고,Let it Crash( 죽게놔둠 ) { throw message as Exception; } else if (message is int) // 서비스코드는 예외에대한 어떠한 키워드도 사용할 필요가 없습니다. { state = (int)message; } else if (message is string) { switch (message as string) { case "get": log.Info("Active Check"); //메시지를 받을수 있는지 체크 Sender.Tell(state); break; } } } protected override void PreRestart(Exception reason, object message) { log.Info("Actor Restart - reason:" + reason.GetType().Name ); //액터 재 시작된이 야기된 이유를 , 재시작시 알수 있습니다. } } |
...
Expand | ||
---|---|---|
| ||
[INFO][2017-09-13 오전 7:34:22][Thread 0029][[akka://ServiceA/user/supervisor#740151541]] CreateChildActor:child1 AKKA뿐아니라, 동시성 메시지 처리가되는 라이브러리를 이해하려면 로그를 잘 파악해야 합니다. 위 로그는 붉은색일때 어떠한 장애가 발생하였으며, Null일때만 파란색 즉 복구처리가 되었고 인자값 예외일때는... 그냥 Child 노드가 정지되게 놔두는 것을 로그로 확인이 가능합니다. 이것은 우리의 의도대로 잘 작동됨을 의미 합니다. |
Info | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
결함 처리를 이렇게 | ||||||||||||||||||||||||
Info | ||||||||||||||||||||||||
결함 처리를 이렇게 어렵게해? 의문을 제기할수 있습니다. 하지만 규모가 큰 있는 서비스코드를 생각해봅시다. 과거 예외 핸들링이 지원안되었을시, retun -1,switch(error) 이러한 코드로 일관성없는 에러처리를 위한 로직을 작성하였고 이것을 개선하고자 예외 처리모델이 나왔습니다. 예외를 발생시키고 , 어느곳에 집결을하여 예외 핸들링을 할수가 있어서 조금더 구조적인 예외 핸들링이 가능해졌습니다. Exception을 세부적으로 설계하고 구조적으로 Catch를 하여 일관성을 유지하는것 그것이 예외처리 모델에 기본이였습니다. 하지만 협업을 하면서 서비스코드내의 예외처리가 일관성이 유지가 되었는지 생각해볼필요가 있습니다. 기존 예외처리모델(try/catch)는 다음과 같은 두가지 문제를 가지고 있습니다. 어느것이 더 합리적이고 심플한 예외대응 처리방법인지 비교해보십시오. 호출자에게 예외를 던지면, 대부분 호출자는 어떻게 처리해야할지 모름다음과 같이, 로컬에 로그가 저장이되면 DB까지 자동으로 전달하는 FileWatcher가 있다고 가정해봅시다.
기존 예외처리모델의 문제는, 호출자에게 책임을 계속 떠넘기는데 있습니다. 호출구조상 Stack의 위부분에는 에러핸들링을 처리 하기위한 충분한 정보가 없습니다. 즉 D과정에 DbBrockenConnectionException 이 발생하고 이과정에서 예외가 발생하여 C의 예외처리기에 갔다고 했을시, C가가진 예외처보및 LogWrite기는 기존 DB접속에대한 정보가 없어서 재시도할 충분한 정보가 없습니다. 이것을 처리하기위해 LogWrite기에게 정보접속을 제공하고 객체를 공유하는것은 단순한 설계를 깨는것이며, 그렇게해서 처리한다고 해도..... try{ 시도} catch{ 또시도 } 이러한 부적절한 코드가 될것입니다. 책임을 지지않는 전역 Catch과거 예외 핸들링이 지원안되었을시, retun -1,switch(error) 이러한 코드로 일관성없는 에러처리를 위한 로직을 작성하였고 이것을 개선하고자 예외 처리모델이 나왔습니다. 예외를 발생시키고 , 어느곳에 집결을하여 예외 핸들링을 할수가 있어서 조금더 구조적인 예외 핸들링이 가능해졌습니다. Exception을 세부적으로 설계하고 구조적으로 Catch를 하여 일관성을 유지하는것 그것이 예외처리 모델에 기본이였습니다. 하지만 협업을 하면서 서비스코드내의 예외처리의 일관성이 유지가 되었는지 생각해볼필요가 있습니다.
장애가 발생했습니다. 하지만 이 처리자는 A,B작업이 어떻게 만들어졌는지 모르는 최근 합류자입니다.
이제는 C에서 처리하지 않은 예외가 D에 전가가 되었습니다.
결국 세부적 예외처리를 포기하고...
가상시나리오이지만, 자신의 서비스 코드에서 예외처리가 얼마나 서비스 흐름을 막고 있는지 생각해볼필요가 있습니다. 이것은 마치 GoTo 문을 고급기법으로 사용을 한것과 다름없습니다. AKKA에서는 부모가 자식에게 예외를 전가시키지 않는 관리감독 전략을 통해 이문제가 복잡해지지 않게 일관성을 유지하려고 장치를 제공하며 후속 플랜도 개발자가 직접 할수있게 추상화를 해놓은것입니다. 자식(정확하게는 호출자)에게 예외를 전가시키지 않는 관리감독 전략을 통해 에러핸들링을 서비스 코드에서 분리하고, 최대한 서비스코드에서 에러처리를 분리하여 상위 부모에서 해결하려는 전략을 사용합니다. 즉 알지못하는 전역 Throw를 굳이 잡으려할필요없이 그냥 죽게두는것입니다. 그리고 부모가 책임진다란 말은 자신의 자신노드가 죽게될시 어떠한 전략을 세울것인가? 우리는 이것에대한 고민을 하면 됩니다이것은 액터생성시 애시당초 탑다운 방식으로 관계도 형성을 명확하게 구조화하기때문에 가능한 전략입니다. |