ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Concurrency] Actor Model
    백엔드/Java 2024. 5. 7. 16:17
    반응형

    * 용어 통일을 위해 Actor, 액터 혼용 대신 Actor로만 사용했습니다.

     

     

    1. 서론


    동시성 프로그래밍 마지막 포스팅으로 Actor 모델이다.

     

    지금까지 내용을 정리하면 다음과 같다.

    • Compare and Swap(CAS)
      단일 값에 대해 원자성을 보장해준다. 하지만 "단일 값"만이다.
    • Software Transactional Memory(STM)
      트랜잭션을 진행하는 동안 다른 메모리 공간에서 공유 자원에 대한 연산을 수행한 뒤 커밋하거나 롤백하여 적용한다.

     

    Actor 모델이 대두된 시기는 역시 STM과 똑같이 멀티코어 프로그래밍으로 인해 화두가 되었다. 다만 STM과는 다르게 처음 Actor 모델을 제안한건 1973년이고, 프로그래밍 언어로 만들어진건 1986년 Erlang 언어로 Actor 모델을 기반으로한 프로그래밍 언어가 만들어졌다. 이후 1990년대에 Ericsson이라는 스웨덴 통신회사에서 Erlang을 사용하여 실제 성능과 안정성이 검증되었고, 2009년에 Akka 프로젝트가 시작되었다는 점에서 STM에 비해 성능과 실용성이 검증된 기술이다.

     

    이번 내용은 Akka 프레임워크를 같이 보면 좋다. 문서가 자세하게 적혀있기 때문에 Actor 모델에 대해 공부하기 쉽게 작성되어 있다.

    https://doc.akka.io/docs/akka/current/general/index.html

     

    General Concepts • Akka Documentation

     

    doc.akka.io


     

     

    2. Thread를 사용하는 프로그래밍의 문제점


    스레드 기반 프로그래밍과 Actor 모델에 대해 찾아보면 다음과 같은 이야기가 나온다.

    Bruce: 그 때(루비를 만들었을 당시)로 돌아간다면, 어떤 기능에 변화를 주고 싶으세요?
    마츠모토 유키히로 (Matz, 루비 언어 제작자): Thread를 없애고, Actor나 진보된 형태의 동시성 기능을 추가했을거에요

    출처: Seven Languages in Seven Weeks
    Havoc Pennington (akka 개발자): Thread를 사용하는 대부분의 프로그램들은 버그로 가득차 있다.

    출처: Dreamforce 2011 
    아무리 실력이 뛰어난 프로그래머라고 해도 일일이 잠금장치(lock)를 이용해서 버그가 없는 멀티쓰레드 코드를 작성하는 것은 불가능하다.

    출처: ZDNET Korea - 액터 모델 프로그래밍 주목하라 (임백준)

     

    참고로 2020년에 출시된 루비 3.0부터는 Ractor라는 Actor 모델을 기반으로한 추상화 객체를 제공해준다.

     

    이외에도 스레드를 직접적으로 조작해서 프로그래밍 하는 것을 회의적으로 바라보는 시각이 많다.

     

    내가 찾아본 Beautiful Concurrency 논문에서 스레드 기반 프로그래밍의 단점은 아래와 같이 정리할 수 있었다.

     

    - 락을 걸고, 해제하는 코드를 직접적으로 적용해야하고, 락을 거는 대상이 여러 개일수록 코드 복잡성이 올라간다.

    - 락을 걸고, 해제하는 행동 자체가 결국 운영체제에서 작동하므로 성능이 좋지 않다.

    - 데드락, 라이브락 문제가 발생한다.

    - 어디에서 버그가 발생하는지 파악하기 힘들고, 재구현하기도 힘들다.

     

    하지만 Actor 모델은 Lock-free 종류중에 하나로 액터 하나가 경량 스레드 하나를 사용해 락 문제에서 자유로워진다. Lock-free가 얼마나 성능이 좋냐면 넥슨 컨퍼런스 발표회에서 간단한 MMORPG 서버를 만들어서 테스트를 해봤는데, Lock-free 자료구조를 사용하면 동접 8000명이 가능하지만, Lock 기반 자료구조를 사용하면 3000명 정도가 한계라고 한다.

     

    경량 스레드 하나를 사용한다고 하면 성능이 안좋다고 생각이 들지 모르지만, Redis는 싱글 스레드로 처리하는데 몇몇 대표적인 명령어는 초당 260,000회 처리가 가능하며 (출처), akka 프레임워크는 문서에서는 경량 스레드를 사용하더라도 고성능을 자랑하고 있으며 초당 50,000,000개의 메시지를 처리할 수 있다고 나와있다. (출처)

     

    물론 모든 상황에서 lock free가 좋은 성능을 내는 것은 아니지만, 일반적으로 스레드가 많아질수록 lock-free 알고리즘의 성능이 더 좋아지긴한다.

     

    * 단점에 대한 더 세부적인 내용은 이전 포스팅 글인 Software Transactional Memory에 코드로 설명되어 있습니다.


     

     

    3. Actor 모델이란?


    Everything is an actor

     

    객체지향의 "Everything is an object"의 철학과 비슷하게, Actor 모델은 "Everything is an actor"라는 철학을 가지고 있다. 그리고 Actor 모델이 처음 제시된 "A Universal Modular ACTOR Formalism for Artificial Intelligence" 논문에서는 Actor가 특정 구현체를 가지고 있는게 아닌 추상적인 개념으로 명시되어 있다. actor를 사람이라고 생각하면서 공부하면 이해하기 매우 좋다. (akka 문서에서도 사람으로 보는게 좋다고 나와있음)

     

     

    "공유"라는 개념을 제거한 모델

     

    Actor의 핵심적인 내용은 "공유"라는 개념을 없애버린 것이다. 트랜잭션의 Isolation처럼 서로 간에 간섭을 할 수도 없고, 공유 자원이라는 것 자체가 존재하지 않는다. 바로 아래 나오는 메시지도 Actor가 "순차적"으로 처리할 수 있게끔 만들었다. 이로 인해 동시성 문제에서 자유로워진다는 장점이 존재한다.  

     


    Actor는 크게 Mailbox, Behavior, State로 구성되어 있다

     

    [1] Mailbox

    https://blog.knoldus.com/how-pass-messages-between-actors-in-akka-actor-system/

     

    Actor는 actor끼리 통신할 수 있으며 actor는 서로 완전히 분리되어 있기 때문에 각자의 메모리를 가지고 있다. Actor끼리 통신하는 방법은 Actor당 하나씩 가지고 있는 Mailbox를 통해서 일을 처리하는 것이다.

     

    위 그림처럼 Actor가 Actor에게 메시지를 보내면 해당 Actor의 Mailbox에 메시지를 비동기로 전달하게 되고, 메시지를 받은 Actor는 메일 박스에 적재된 메시지 순서대로 일을 처리하게 된다. 

     

    그래서 메시지를 보내는 Actor나 메시지를 받는 Actor 모두 메시지 작업을 기다릴 필요 없이 각자 할 일을 하면 되므로 Non-blocking 방식이라 불린다.

     

    [2] Behavior

    Actor가 메시지를 받았을 때 취하는 행동이 Behavior이다. 쉽게 말하면 객체의 메서드 역할을 한다. Actor가 가지고 있는 값을 변경할 수도 있고, Chile Actor를 생성/제거를 하거나, 다른 Actor에게 메시지를 전달하는 등 여러가지 행동을 뜻한다.

     

    [3] State

    Actor 자기자신만 업데이트 할 수 있는 캡슐화된 자체 내부 상태를 State라고 부른다.

    쉽게 말해서 완전히 객체지향적으로 코드를 작성한 객체가 있을 때, 객체 내부 변수값을 말한다.

    Actor는 자신이 수신한 메시지를 통해 값을 수정할 수 있지만, 다른 Actor가 직접적으로 수정할 수 없고 이벤트를 보내야한다.


     

     

    4. 여러가지 정보들


    Akka framework 기준으로 작성했습니다.

     

    [1] Actor의 최대 생성 개수

    Actor는 용량이 될 수 있는한 "무제한"으로 만들 수 있다.

    Akka의 Actor는 기본이 400 byte이기 때문에 사실상 수만개는 거뜬히 만들 수 있다.

     

     

    [2] Mailbox의 용량

    Mailbox의 용량은 기본적으로 "무제한"으로 설정되어있다. 하지만 메모리 용량이 부족한 상황도 생기므로 이때에는 deadletters로 메시지를 전달하게 된다.

     

     

    [3] deadletters

    메시지를 전달할 수 없거나, 에러가 발생하면 메시지를 deadletters로 보내게 된다. 이때 예외적으로 불안정한 네트워크로 인해 메시지가 손실되는 것은 deadletters로 보내지지 않고, 개발자가 따로 재전송이나 에러 처리를 해줘야한다.

     

     

    [4] Falut Tolerate (장애 허용)

    akka는 "let it crash"로 실패하면 실패하게 두자는게 원칙이다. 여기서 Supervision이라는걸 사용한다. 단어 그대로 감독하는 일과 비슷하다.

    Akka에서는 Actor는 Child Actor를 만들 수 있는데, Child Actor에 문제가 발생하면 Supervisor인 Parent Actor에게 알리고, Parent Actor는 실패에 맞는 전략을 선택할 수 있다.

     

    import akka.actor.OneForOneStrategy
    import akka.actor.SupervisorStrategy._
    import scala.concurrent.duration._
    
    override val supervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
        case _: ArithmeticException      => Resume
        case _: NullPointerException     => Restart
        case _: IllegalArgumentException => Stop
        case _: Exception                => Escalate
      }

     

    akka 공식 문서에서 Java 코드는 좀 보기 힘든데, Scala 코드는 깔끔해서 가져왔다.

    첫 번째 케이스만 설명하면 ArithmeticException이 발생할 경우 Resume으로 처리한다는 코드다.

     

    옵션은 네 가지로 이 링크에서 볼 수 있고, 자바에서의 실제 행동은 이 링크를 참조하면 된다.

     

    • [Resume] Resume the subordinate, keeping its accumulated internal state

    실패를 무시하고 다음 메시지를 처리한다

    • [Restart] Restart the subordinate, clearing out its accumulated internal state

    새로운 Actor 인스턴스를 만들어서 다시 시작한다.

    • [Stop] Stop the subordinate permanently

    Child Actor를 영구적으로 중지한다.

    • [Escalate] Escalate the failure, there by failing itself

    Supervisor인 Parent Actor도 실패 처리한다.


     

     

     

    5. 출처 및 보면 좋은 링크


    - akka 공식 문서

    - ZDNET Korea - 액터 모델 프로그래밍에 주목하라

    - JBugKorea 발표 슬라이드 - 맛만 보자 액터 모델이란

    - 데브시스터즈 기술 블로그 - 9가지 프로그래밍 언어로 배우는 개념: 5편 - 동시성 프로그래밍

    - Syntax Sugar 티스토리 블로그 - Concurrency with Actor Model (행위자 모델)

    - 넥슨 개발자 컨퍼런스 2014 시즌2: 멀티스레드 프로그래밍이 왜 이리 힘든가요?

     

    한국어로 된 내용중에 더 자세한 내용을 공부하고 싶은 분은 아래 블로그를 추천합니다.

    https://velog.io/@leesomyoung/Akka-Akka%EB%9E%80

     

    [Akka] Akka란?

    Akka는 Open Source Tool Kit으로 JVM 상에서 동시성과 분산 애플리케이션을 단순화하는 런타임이다. Erlang(범용 병렬 프로그래밍 언어 / 함수형 언어)으로부터 영향을 받아 Actor 기반의 동시성이 두드러

    velog.io


     

     

    반응형

    댓글

Designed by Tistory.