스칼라

import akka.actor.{Actor, ActorSystem, Props}

// 액터 정의
class HelloActor extends Actor {
  def receive: Receive = {
    case "hello" =>
      sender() ! "world"
    case msg =>
      println(s"Unknown message: $msg")
  }
}

// 메인 또는 테스트 앱
object HelloActorApp extends App {
  // 액터 시스템 생성
  val system = ActorSystem("HelloSystem")

  // 액터 인스턴스 생성

  val helloActor = system.actorOf(Props[HelloActor], name = "helloActor")

  // 응답을 기다릴 임시 액터 생성
  import akka.pattern.ask
  import akka.util.Timeout
  import scala.concurrent.duration._
  import scala.concurrent.Await
  import scala.concurrent.ExecutionContext.Implicits.global

  implicit val timeout: Timeout = Timeout(5.seconds)

  // 메시지 전송 및 응답 대기
  val future = helloActor ? "hello"
  val result = Await.result(future, timeout.duration)

  println(s"응답: $result") // "world" 출력

  // 종료
  system.terminate()
}

주요 설명:



자바

public class HelloActor extends AbstractActor {

    // 액터 생성 팩토리
    public static Props props() {
        return Props.create(HelloActor.class);
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
            .matchEquals("hello", msg -> {
                getSender().tell("world", getSelf());
            })
            .matchAny(msg -> {
                System.out.println("Unknown message: " + msg);
            })
            .build();
    }
}

public class HelloActorApp {
    public static void main(String[] args) throws Exception {
        ActorSystem system = ActorSystem.create("HelloSystem");

        ActorRef helloActor = system.actorOf(HelloActor.props(), "helloActor");

        // 메시지 보내고 응답 기다리기
        Future<Object> future = Patterns.ask(helloActor, "hello", 5000);
        String result = (String) Await.result(future, Duration.create(5, TimeUnit.SECONDS));

        System.out.println("응답: " + result); // "world"

        system.terminate();
    }
}



닷넷

public class HelloActor : ReceiveActor
{
    public HelloActor()
    {
        Receive<string>(msg =>
        {
            if (msg == "hello")
            {
                Sender.Tell("world");
            }
            else
            {
                Console.WriteLine($"Unknown message: {msg}");
            }
        });
    }
}

class Program
{
    static async Task Main(string[] args)
    {
        using var system = ActorSystem.Create("HelloSystem");

        var helloActor = system.ActorOf<HelloActor>("helloActor");

        // Ask 패턴으로 메시지 보내고 응답 받기
        var response = await helloActor.Ask<string>("hello", TimeSpan.FromSeconds(5));

        Console.WriteLine($"응답: {response}"); // "world"

        await system.Terminate();
    }
}



코틀린 AKKA 버전

class HelloActor : AbstractActor() {

    override fun createReceive(): Receive {
        return receiveBuilder()
            .matchEquals("hello") {
                sender.tell("world", self)
            }
            .matchAny {
                println("Unknown message: $it")
            }
            .build()
    }

    companion object {
        fun props(): Props = Props.create(HelloActor::class.java)
    }
}

fun main() {
    val system = ActorSystem.create("HelloSystem")
    val helloActor = system.actorOf(HelloActor.props(), "helloActor")

    val future = Patterns.ask(helloActor, "hello", 5000)
    val result = Await.result(future, Duration.create(5, TimeUnit.SECONDS)) as String

    println("응답: $result") // world 출력

    system.terminate()
}



코틀린 액터 코어버전

코틀린은 액터모델을 채택했기때문에 서드파티툴없이 액터모델 이용가능합니다. 

(akka,pekko  이용시 액터모델이 스트림,클러스터로 확장되는 툴은없기때문에 직접 구현해야함) 

import kotlinx.coroutines.channels.actor

// 메시지 정의
sealed class Message
data class Greet(val replyTo: CompletableDeferred<String>) : Message
object Unknown : Message

// 액터 함수
fun CoroutineScope.helloActor() = actor<Message> {
    for (msg in channel) {
        when (msg) {
            is Greet -> msg.replyTo.complete("world")
            else -> println("Unknown message received: $msg")
        }
    }
}

fun main() = runBlocking {
    val actor = helloActor()

    val response = CompletableDeferred<String>()
    actor.send(Greet(response))

    println("응답: ${response.await()}") // 출력: world

    actor.close()
}



파이썬 Ray기반

// hello_actor.py
import ray

# Ray 초기화
ray.init()

# 액터 클래스 정의
@ray.remote
class HelloActor:
    def say(self, msg):
        if msg == "hello":
            return "world"
        else:
            return f"Unknown message: {msg}"

// main.py
import ray
from hello_actor import HelloActor

ray.init(ignore_reinit_error=True)  # 이미 실행 중일 경우 무시

# 액터 생성
actor = HelloActor.remote()

# 메시지 전송 및 응답 받기
response = ray.get(actor.say.remote("hello"))
print(f"응답: {response}")  # 출력: world

# 종료
ray.shutdown()



Erlang(언랭) 

-module(hello_actor).
-export([start/0, loop/0]).

start() ->
    spawn(fun loop/0).

loop() ->
    receive
        {From, "hello"} ->
            From ! "world",
            loop();
        {From, Msg} ->
            From ! {error, "Unknown message: " ++ Msg},
            loop()
    end.


1> c(hello_actor). % 컴파일
2> Pid = hello_actor:start().  % 액터 시작
3> Pid ! {self(), "hello"}.    % 메시지 전송
4> flush().                    % 응답 확인

Shell output:
Shell got "world"

장점 (Erlang식 액터 모델 특징)



액터프로그래밍 활용사례

1. Scala: Akka 프레임워크

Akka는 Scala와 Java에서 사용 가능한 강력한 액터 기반 프레임워크로, 분산 시스템과 동시성 처리를 위한 도구를 제공합니다.

2. Java: Akka 및 Spring Integration

Java 진영에서는 Akka 외에도 Spring Integration과 같은 프레임워크를 통해 액터 모델의 개념을 적용하고 있습니다.

3. C#: Akka.NET

Akka.NET은 .NET 환경에서 Akka의 기능을 제공하는 프레임워크로, C#과 F#에서 사용 가능합니다.

4. Kotlin: 코루틴과 채널

Kotlin에서는 언어 자체의 **코루틴(coroutines)**과 **채널(channels)**을 활용하여 액터 모델을 구현할 수 있습니다.

5. Python: Ray 프레임워크

Ray는 Python에서 분산 컴퓨팅을 위한 프레임워크로, 액터 모델을 지원하여 대규모 병렬 처리를 가능하게 합니다.

6. Erlang: 내장 액터 시스템

Erlang은 언어 자체에 액터 모델이 내장되어 있어, 높은 동시성과 분산 시스템 구축에 적합합니다.

7. Elixir: Erlang 기반의 액터 모델

Elixir는 Erlang VM 위에서 동작하는 언어로, 액터 모델을 활용하여 확장성과 유지보수성이 높은 애플리케이션을 개발할 수 있습니다.

이처럼 다양한 언어와 프레임워크에서 액터 모델이 구현되어, 여러 테크 기업에서 분산 시스템과 동시성 처리를 위한 핵심 도구로 활용되고 있습니다.



AKKA의 다양한 장치를 이용 액터기능을 확장하기

코틀린 버전 AkkaStream을 활용해 BackPresure를 포함 TPS제어

class HelloActorStreamTest {

    companion object {
        private val system = ActorSystem.create("TestSystem")
        private val materializer = ActorMaterializer.create(system)

        @JvmStatic
        @AfterClass
        fun teardown() {
            TestKit.shutdownActorSystem(system)
        }
    }

    @Test
    fun `should throttle and respond with world`() {
        object : TestKit(system) {
            init {
                val helloActor = system.actorOf(HelloActor.props())

                // Source.queue + throttle + ask pattern
                val (queue, done) = Source.queue<String>(10, OverflowStrategy.backpressure())
                    .throttle(3, JDuration.ofSeconds(1)) // 초당 3건
                    .mapAsync(1) { msg ->
                        akka.pattern.Patterns.ask(helloActor, msg, 1000).thenApply { it as String }
                    }
                    .toMat(Sink.foreach { response ->
                        println("응답 수신: $response")
                        expectMsgEquals("world")  // 테스트 검증
                    }, Keep.both())
                    .run(materializer)

                // 메시지 전송 (10건)
                for (i in 1..10) {
                    queue.offer("hello")
                }

                // 일부 응답 대기
                within(Duration.create(5, "seconds")) {
                    // 최소 3건은 들어왔는지 확인
                    for (i in 1..3) {
                        expectNoMessage()
                    }
                }
            }
        }
    }
}


액터를 라우터를 구성후 순차 라운드로빈처리

                    +------------------+
                    |   Router (RoundRobinPool)   |
                    +------------------+
                    /        |        \
          HelloActor1  HelloActor2  HelloActor3

fun main() {
    val system = ActorSystem.create("RoundRobinSystem")

    // RoundRobinPool로 HelloActor 3개 구성
    val router = system.actorOf(
        RoundRobinPool(3).props(HelloActor.props()),
        "helloRouter"
    )

    println("=== 메시지 전송 시작 ===")
    for (i in 1..9) {
        val future = Patterns.ask(router, "hello $i", 1000)
        val result = Await.result(future, Duration.create(1, TimeUnit.SECONDS))
        println("응답 $i: $result")
    }

    system.terminate()
}

// Output
[helloRouter/$a] received: hello 1
응답 1: world
[helloRouter/$b] received: hello 2
응답 2: world
[helloRouter/$c] received: hello 3
응답 3: world
[helloRouter/$a] received: hello 4
응답 4: world
...






도큐먼트

🟦 Scala / Java – Akka


🟥Kotlin – Kotlinx Coroutines + Channels


🟩 C# / .NET – Akka.NET


🟨 Python – Ray


🟥 Erlang – OTP / Built-in Actors


🟪 Elixir – OTP / GenServer


🟦 Scala/Java – Apache Pekko

🔸 소개

🔸 특징

🔸 적용 분야

🔸 공식 문서


🟩 C# – Microsoft Orleans

🔸 소개

🔸 특징

🔸 적용 분야

🔸 공식 문서