Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu những khái niệm cơ bản về Akka Actor trong Scala.
Cấu trúc phân cấp
Các Actor là các object dùng đóng gói trạng thái và hành vi, chúng giao tiếp với nhau bằng các thông báo. Chúng ta có thể coi Actor như một người.
Giống như một team phát triển phần mềm, Actor cũng phân cấp. Trong một team có Project Manager, người nhận các yêu cầu từ khách hàng và chia nó cho các leader trong team. Sau đó, những leader này sẽ tiếp tục phân chia công việc cho các thành viên trong nhóm. Các thành viên trong nhóm sau khi hoàn thành công việc sẽ thông báo cho leader về trạng thái của nó
Tương tự như vậy, Actor cũng có thể muốn chia một việc ra thành nhiều việc nhỏ hơn. Actor tạo Actor con và bằng mọi cách nhưng Actor con không thể thực hiện thành công thì nó sẽ gửi thông báo lỗi phù hợp về cho Actor cha, nơi tạo ra nó.
Actor System
Một Actor System quản lý các tài nguyên của nó được cấu hình để sử dụng với mục đích chạy các Actor mà nó chứa đựng.
Một Actor System giống như một nhóm tập hợp các Actor là một khối dùng để quản lý các công cụ chia sẻ như là các service lập lịch chạy, cấu hình, ghi log…
Các thành phần của Actor System
- User Guardian Actor: Là Actor cha của tất cả các Actor mà nó tạo ra.
- System Guardian Actor: là Actor cha của tất cả các Actor mà do Akka tạo ra.
- Dead Letter Office: Nó là một Actor giống như những Actor khác nhưng mỗi lần một thông báo gửi đến một Actor nhưng không tồn tại hoặc đang chạy thì sẽ được chuyển đến Dead Letter Office
- Scheduler: Được sử dụng để đặt lịch hẹn chạy các task
Tạo một Actor bằng Props
Props là một class cấu hình để chỉ định các tuỳ chọn khi tạo các Actor
Dưới đây là một vài ví dụ làm thế nào để tạo các Props instance
val props1 = Props[MyActor] val props2 = Props(new ActorWithArgs(“args”)) val prop3 = Props(classOf[ActorWithArgs], “arg”)
Các tiếp cận được đề xuất khi tạo các Actor dùng Props là không hỗ trợ cho các trường hợp hàm tạo của Actor nhận các class giá trị như tham số
Khai báo một Actor trong một Actor khác là rất nguy hiểm và làm hỏng đóng gói Actor. Không bao giờ truyền tham chiếu này của Actor vào một Props
Đề xuất là bạn có định nghĩa phương thức Props bên trong một Companion Object
object MyActor { def props(num: Int): Props = Props(new MyActor(num)) } class MyActor(num: Int) extends Actor { def receive = { case x: Int => sender() ! (x + num) } } class MyAnotherActor extends Actor { context.actorOf(MyActor.props(10), "my-actor") }
Cũng có thể khai báo các Message nào một Actor sẽ nhận trong Companion Object của Actor
object MyActor { case class Greeting(from: String) case object Bye } class MyActor extends Actor { import MyActor._ def receive = { case Greeting(greeter) => println(s”Hello $greeter”) case Bye => println(s”Good by everyone”) } }
Tell và Ask
Có 2 cách để nói chuyện với một Actor
- Tell (!)
- Ask (?)
Tell (!) : Chạy và quên đi, nghĩa là bạn gửi một thông báo bất đồng bộ và không quan tâm đến kết quả
Ask (?): gửi thông báo bất đồng bộ nhưng không giống như Tell nó sẽ nhận kết quả trong tương lai
object MyActor { case class Sum(a: Int, b: Int) } class MyActor extends Actor { override def receive: Receive = { case Sum(a: Int, b: Int) => sender() ! (a + b) case _ => sender() ! 0 } } object TellAndAsk extends App { implicit val duration: Timeout = 20 seconds val system = ActorSystem("ask-and-tell") val myActor = system.actorOf(Props[MyActor], "my-actor") val sum: Future[Int] = (myActor ? Sum(3,4)).asInstanceOf[Future[Int]] println("Sum of 3 and 4 is " + Await.result(sum, Duration.Inf)) system.terminate() }
ActorRef, ActorSelection và ActorPath
ActorRef là interface cho Actor System và nó là tham chiếu cho một Actor đơn. Khi một Actor stop ActorRef, nó bắt đầu trỏ đến Dead Letter Office, nghĩa là bất kỳ thông báo nào gửi đến Actor đó thì nó sẽ được chuyển đến Dead Letter Office. Mỗi lần chúng ta tạo một Actor mới, Akka sẽ tạo tham chiếu Actor mới. Chúng ta có thể tạo một tham chiếu Actor mới sử dụng phương thức actorOf
val system = ActorSystem(“my-actor-system”) /* actor ở đây là một tham chiếu actor*/ val actor = system.actorOf(Prop[MyActor])
Đường dẫn Actor (ActorPath)
Actor được tạo theo cách phân cấp chặt chẽ. Tất cả các Actor trong Actor system đều được đặt với tên là duy nhất. Như đã nói ở trên, tất cả các Actor đều có Actor cha hoặc nó là một User Guardian hoặc Actor khác tạo Actor. Nên đường dẫn cho bất kỳ Actor nào cũng đều giống như một file hệ thống mà bắt đầu với root(/) và sau đó user sẽ dùng theo sau root bởi vì tất cả các Actor mà chúng ta tạo đều là con của User Guardian.
Chúng ta tạo một Actor với tên là my-actor thì đường dẫn cho my-actor sẽ là
/user/my-actor
Tương tự như thế nếu chúng ta tạo bất kỳ Actor nào từ my-actor thì nó sẽ là cha của Actor mới với đường dẫn như sau:
/user/my-actor/
Lựa chọn Actor (ActorSelection)
Lựa chọn Actor là cách khác để diễn tả một Actor. Giống như actorRef, chúng ta có thể gửi thông báo qua ActorSelection nhưng khác là nó được tạo từ ActorPath
system.actorSelection("/user/parent/child")
ActorSelection vẫn có hiệu lực ngay cả khi Actor chết