Actor model framework


Actor model 프레임워크

액터 모델이란

액터 모델(Actor model)은 동시성 프로그래밍을 위한 모델이다. 1973년 Carl Hewitt가 제안했고, Erlang과 Akka를 통해 널리 알려졌다.

멀티스레드 프로그래밍에서는 여러 스레드가 같은 메모리(상태)를 공유하기 때문에 락(lock)으로 동시 접근을 막아야 한다. 락을 잘못 쓰면 데드락이나 경쟁 상태(race condition)가 생기고, 코드도 복잡해진다.

액터 모델은 상태를 공유하지 않는 것으로 이 문제를 피한다.

액터의 구성

액터는 아래 세 가지를 가진 독립적인 단위다.

  • 상태(State): 액터 내부에만 존재하고 외부에서 직접 접근할 수 없다.
  • 행동(Behavior): 메시지를 받았을 때 무엇을 할지 정의한다.
  • 메일박스(Mailbox): 받은 메시지가 쌓이는 큐다.
  ┌──────────── Actor A ────────────┐
  │  Mailbox: [msg3][msg2][msg1] ───┼─▶ 한 번에 하나씩 처리
  │  State:   (외부 접근 불가)       │
  │  Behavior                        │
  └──────────────────────────────────┘
          │ 메시지 전송 (비동기)
          ▼
  ┌──────── Actor B ────────┐
  │  Mailbox / State / ...   │
  └──────────────────────────┘

액터는 메시지를 받으면 다음 세 가지 일만 할 수 있다.

  1. 다른 액터에게 메시지를 보낸다.
  2. 새로운 액터를 만든다.
  3. 다음 메시지를 처리할 때의 행동(상태)을 바꾼다.

왜 락이 필요 없나

  • 액터는 메일박스의 메시지를 한 번에 하나씩 순서대로 처리한다. 그래서 액터 내부 상태는 동시에 접근될 일이 없다.
  • 다른 액터의 상태를 바꾸고 싶으면 직접 수정하지 않고 메시지를 보낸다.
  • 메시지 전송은 비동기라서 보낸 쪽은 응답을 기다리며 블로킹되지 않는다.

결국 동시성은 “여러 액터가 각자 메시지를 처리하는 것”으로 표현되고, 하나의 액터 안은 싱글 스레드처럼 단순하게 작성할 수 있다.

장애 처리 - Let it crash

액터들은 부모-자식 관계로 계층을 이룬다. 자식 액터에서 에러가 나면 부모 액터(슈퍼바이저)가 재시작, 중지, 상위로 전파 중 하나를 결정한다.

그래서 액터 안에서 모든 예외를 방어적으로 처리하기보다는 실패하면 그냥 죽게 두고 슈퍼바이저가 복구하게 하는 Let it crash 철학을 따른다. Erlang에서 나온 개념이다.

위치 투명성

액터는 주소(참조)로만 메시지를 주고받기 때문에, 상대 액터가 같은 프로세스에 있든 다른 서버에 있든 코드가 같다. 그래서 액터 모델은 분산 시스템으로 확장하기 쉽다.

대표 프레임워크

프레임워크언어특징
Erlang/OTPErlang, Elixir언어 차원에서 액터(프로세스) 지원. 통신 장비, WhatsApp 등에서 사용
AkkaScala, JavaJVM 대표 액터 프레임워크. 클러스터링, 슈퍼비전 지원
Microsoft OrleansC# (.NET)“가상 액터(Grain)” 개념. 액터 생성/소멸을 런타임이 관리
Proto.ActorGo, C#Akka에서 영감을 받은 크로스 플랫폼 프레임워크

Node.js는 이벤트 루프가 싱글 스레드라 액터 모델이 기본은 아니지만, 메시지 큐(SQS, Redis Stream 등)로 워커를 나누는 구조가 비슷한 아이디어라고 볼 수 있다.

장단점

장점

  • 락 없이 동시성을 다룰 수 있어 코드가 단순해진다.
  • 위치 투명성 덕분에 수평 확장(분산)이 쉽다.
  • 슈퍼비전으로 장애를 격리하고 복구할 수 있다.

단점

  • 메시지가 비동기로 오가기 때문에 흐름을 추적하고 디버깅하기 어렵다.
  • 메시지 순서나 전달 보장(at-most-once 등)을 직접 고려해야 한다.
  • 단순한 CRUD 서비스에는 과한 구조일 수 있다.

참고 링크