How to implement actor model without Akka?

前端 未结 2 1888
春和景丽
春和景丽 2020-12-08 23:49

How to implement simple actors without Akka? I don\'t need high-performance for many (non-fixed count) actor instances, green-threads, IoC (lifecycle, Props-based factories,

相关标签:
2条回答
  • 2020-12-09 00:15

    This will use FixedThreadPool (and so its internal task queue):

    import scala.concurrent._
    
    trait Actor[T] {
      implicit val context = ExecutionContext.fromExecutor(java.util.concurrent.Executors.newFixedThreadPool(1))
      def receive: T => Unit
      def !(m: T) = Future { receive(m) }
    }
    

    FixedThreadPool with size 1 guarantees sequentiality here. Of course it's NOT the best way to manage your threads if you need 100500 dynamically created actors, but it's fine if you need some fixed amount of actors per application to implement your protocol.

    Usage:

    class Ping(pong: => Actor[Int])  extends Actor[Int] {     
          def receive = {
              case m: Int => 
                 println(m)
                 if (m > 0) pong ! (m - 1)
          }    
    }
    
    object System { 
          lazy val ping: Actor[Int] = new Ping(pong) //be careful with lazy vals mutual links between different systems (objects); that's why people prefer ActorRef
          lazy val pong: Actor[Int] = new Ping(ping)
    }
    
    System.ping ! 5
    

    Results:

    import scala.concurrent._
    defined trait Actor
    defined class Ping
    defined object System
    res17: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@6be61f2c
    5
    4
    3
    2
    1
    0
    
    scala> System.ping ! 5; System.ping ! 7
    5
    7
    4
    6
    3
    5
    2
    res19: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@54b053b1
    4
    1
    3
    0
    2
    1
    0
    

    This implementation is using two Java threads, so it's "twice" faster than counting without parallelization.

    0 讨论(0)
  • 2020-12-09 00:17

    Here is most minimal and efficient actor in the JVM world with API based on Minimalist Scala actor from Viktor Klang: https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/src/test/scala/com/github/gist/viktorklang/Actor.scala

    It is handy and safe in usage but isn't type safe in message receiving and cannot send messages between processes or hosts.

    Main features:

    • simplest FSM-like API with just 3 states (Stay, Become and Die): https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/src/test/scala/com/github/gist/viktorklang/Actor.scala#L28-L30

    • minimalistic error handling - just proper forwading to the default exception handler of executor threads: https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/src/test/scala/com/github/gist/viktorklang/Actor.scala#L52-L53

    • fast async initialization that takes ~200 ns to complete, so no need for additional futures/actors for time consuming actor initialization: https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/out0.txt#L447

    • smallest memory footprint, that is ~40 bytes in a passive state (BTW the new String() spends the same amout of bytes in the JVM heap): https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/out0.txt#L449

    • very efficient in message processing with throughput ~90M msg/sec for 4 core CPU: https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/out0.txt#L466

    • very efficient in message sending/receiving with latency ~100 ns: https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/out0.txt#L472

    • per actor tuning of fairness by the batch parameter: https://github.com/plokhotnyuk/actors/blob/41eea0277530f86e4f9557b451c7e34345557ce3/src/test/scala/com/github/gist/viktorklang/Actor.scala#L32

    Example of stateful counter:

      def process(self: Address, msg: Any, state: Int): Effect = if (state > 0) { 
         println(msg + " " + state)
         self ! msg
         Become { msg => 
            process(self, msg, state - 1)
         }
      } else Die
    
      val actor = Actor(self => msg => process(self, msg, 5))
    

    Results:

    scala> actor ! "a"
    a 5
    
    scala> a 4
    a 3
    a 2
    a 1
    
    0 讨论(0)
提交回复
热议问题