2013-06-12 25 views
15

Tôi có một diễn viên tạo một diễn viên trẻ để thực hiện một số phép tính dài.Làm thế nào để đối phó với việc khởi tạo lâu dài của một diễn viên trẻ Akka?

Sự cố là khởi tạo diễn viên con mất vài giây và tất cả thư mà diễn viên chính gửi cho con giữa nó được tạo và được khởi tạo đầy đủ sẽ bị loại bỏ.

Đây là logic của mã mà tôi đang sử dụng:

class ChildActor extends Actor { 
    val tagger = IntializeTagger(...) // this takes a few seconds to complete 

    def receive = { 
    case Tag(text) => sender ! tagger.tag(text) 
    case "hello" => println("Hello") 
    case _ => println("Unknown message") 
    } 
} 

class ParentActor extends Actor { 
    val child = context.ActorOf(Props[ChildActor], name = "childactor") 

    // the below two messages seem to get lost 
    child ! "hello" 
    child ! Tag("This is my sample text") 

    def receive = { 
    ... 
    } 
} 

Làm thế nào tôi có thể khắc phục vấn đề đó? Có thể làm cho phụ huynh diễn viên chờ cho đến khi đứa trẻ được khởi tạo hoàn toàn không? Tôi sẽ sử dụng diễn viên con với định tuyến và có thể trên các hệ thống diễn viên từ xa.

EDIT

Sau lời khuyên drexin của tôi có thay đổi mã của tôi vào:

class ChildActor extends Actor { 
    var tagger: Tagger = _ 

    override def preStart() = { 
    tagger = IntializeTagger(...) // this takes a few seconds to complete 
    } 

    def receive = { 
    case Tag(text) => sender ! tagger.tag(text) 
    case "hello" => println("Hello") 
    case _ => println("Unknown message") 
    } 
} 

class ParentActor extends Actor { 
    var child: ActorRef = _ 

    override def preStart() = { 
    child = context.ActorOf(Props[ChildActor], name = "childactor") 

    // When I add 
    // Thread.sleep(5000) 
    // here messages are processed without problems 

    // wihout hardcoding the 5 seconds waiting 
    // the below two messages seem to get lost 
    child ! "hello" 
    child ! Tag("This is my sample text") 
    } 

    def receive = { 
    ... 
    } 
} 

nhưng vấn đề vẫn còn. Tôi đang thiếu gì?

+0

Phiên bản nào của AKKA bạn đang sử dụng. Tôi gặp khó khăn trong việc mô phỏng vấn đề của bạn. Tất cả mọi thứ dường như làm việc tốt cho tôi. – cmbaxter

+0

@cmbaxter: Tôi đang sử dụng akka 2.2-M2. – twowo

Trả lời

17

Không khởi tạo tagger trong hàm tạo, nhưng trong móc preStart, theo cách này, thư sẽ được thu thập trong hộp thư và được phân phối khi diễn viên sẵn sàng.

chỉnh sửa:

Bạn nên làm tương tự cho việc tạo diễn viên trong lớp ParentActor của bạn, bởi vì bạn sẽ có cùng một vấn đề, nếu ChildActor sẽ phản ứng, trước khi ParentActor được khởi tạo.

edit2:

Tôi tạo ra một ví dụ đơn giản, nhưng tôi đã không thể tái tạo vấn đề của mình. Mã sau công trình hoàn toàn tốt đẹp:

import akka.actor._ 

case class Tag(x: String) 

class ChildActor extends Actor { 
    type Tagger = String => String 
    var tagger: Tagger = _ 

    override def preStart() = { 
    tagger = (x: String) => x+"@tagged" // this takes a few seconds to complete 
    Thread.sleep(2000) // simulate time taken to initialize Tagger 
    } 

    def receive = { 
    case Tag(text) => sender ! tagger(text) 
    case "hello" => println("Hello") 
    case _ => println("Unknown message") 
    } 
} 

class ParentActor extends Actor { 
    var child: ActorRef = _ 

    override def preStart() = { 
    child = context.actorOf(Props[ChildActor], name = "childactor") 

    // When I add 
    // Thread.sleep(5000) 
    // here messages are processed without problems 

    // wihout hardcoding the 5 seconds waiting 
    // the below two messages seem to get lost 
    child ! "hello" 
    child ! Tag("This is my sample text") 
    } 

    def receive = { 
    case x => println(x) 
    } 
} 

object Main extends App { 

    val system = ActorSystem("MySystem") 

    system.actorOf(Props[ParentActor]) 
} 

Output là:

[info] Running Main 
Hello 
This is my sample [email protected] 
+0

Tôi sợ rằng điều này không giải quyết được vấn đề của tôi. Tôi đã cập nhật bài đăng để giải thích thêm. – twowo

+0

cập nhật bài viết của tôi – drexin

+0

Cảm ơn câu trả lời của bạn, nhờ đó tôi nhận ra vấn đề của mình là gì: Tôi đã làm system.shutdown() trong ứng dụng chính của mình trước khi diễn viên có cơ hội trả lời! Đó là ngu ngốc nhưng đã cho tôi một thời gian dài để tìm ra. Cảm ơn bạn đã nỗ lực, tôi đánh dấu câu trả lời của bạn là đã được chấp nhận. – twowo

0

tôi sẽ đề nghị để gửi một "sẵn sàng" Thông điệp từ diễn viên nhí cho phụ huynh và bắt đầu gửi tin nhắn đến các diễn viên nhí chỉ sau tin nhắn này sẽ được nhận. Bạn có thể làm điều đó chỉ trong phương thức cho các trường hợp sử dụng đơn giản hoặc bạn có thể sử dụng become hoặc FSM để thay đổi hành vi của diễn viên phụ sau khi trẻ được khởi tạo (ví dụ: lưu trữ thư cho trẻ trong bộ nhớ trung gian và gửi tất cả thư đó khi nó sẽ sẵn sàng).

+0

Tôi hiểu quan điểm của bạn nhưng nếu nam diễn viên hiện đang là trẻ em được đưa ra không phải từ bên trong một diễn viên khác mà chỉ từ chủ đề chính? Phải có một cách dễ dàng hơn để đối phó với tình hình, bạn không nghĩ sao? – twowo

+0

Sau đó, các tin nhắn có thể được lưu trữ trong "con" diễn viên (như cmbaxter đề nghị). –

7

Tôi nghĩ rằng những gì bạn có thể tìm kiếm là kết hợp của Stashbecome. Ý tưởng sẽ là diễn viên con sẽ thiết lập trạng thái ban đầu của nó thành uninitialized, và trong khi ở trạng thái này, nó sẽ stash tất cả các tin nhắn đến cho đến khi nó được khởi tạo hoàn toàn. Khi nó được khởi tạo hoàn toàn, bạn có thể bỏ chặn tất cả các tin nhắn trước khi trao đổi hành vi cho trạng thái khởi tạo. Một ví dụ đơn giản như sau:

class ChildActor2 extends Actor with Stash{ 
    import context._ 
    var dep:SlowDependency = _ 

    override def preStart = { 
    val me = context.self 
    Future{ 
     dep = new SlowDependency 
     me ! "done" 
    } 
    } 

    def uninitialized:Receive = { 
    case "done" => 
     unstashAll 
     become(initialized) 
    case other => stash() 
    } 

    def initialized:Receive = { 
    case "a" => println("received the 'a' message") 
    case "b" => println("received the 'b' message") 
    } 

    def receive = uninitialized 
} 

Chú ý trong preStart rằng tôi đang làm khởi của tôi không đồng bộ, để không ngăn chặn sự khởi động của các diễn viên. Bây giờ đây là một chút xấu xí, với đóng cửa trên biến thể dep var.Bạn chắc chắn có thể xử lý nó bằng cách thay vì gửi một thông điệp tới một diễn viên khác xử lý sự khởi tạo của sự phụ thuộc chậm và gửi nó trở lại diễn viên này. Khi nhận được sự phụ thuộc, sau đó nó sẽ gọi become cho trạng thái initialized.

Bây giờ có một caveat với Stash và tôi sẽ dán nó vào ngay từ các tài liệu Akka:

Please note that the Stash can only be used together with actors that 
have a deque-based mailbox. For this, configure the mailbox-type of the 
dispatcher to be a deque-based mailbox, such as 
akka.dispatch.UnboundedDequeBasedMailbox (see Dispatchers (Scala)). 

Bây giờ nếu điều này không phù hợp với bạn, bạn có thể thử một nhiều DI kiểu cách tiếp cận và để cho các phụ thuộc chậm được tiêm vào diễn viên con thông qua constructor của nó. Vì vậy, bạn sẽ xác định các diễn viên nhí như vậy:

class ChildActor(dep:SlowDependency) extends Actor{ 
    ... 
} 

Sau đó, khi bắt đầu lên diễn viên này, bạn sẽ làm điều đó như sau:

context.actorOf(new Props().withCreator(new ChildActor(slowDep)), name = "child-actor") 
+0

Trước tiên, bạn không bao giờ nên đóng trạng thái có thể thay đổi và thứ hai, bạn không bao giờ nên đóng trên 'ActorContext'. Việc gọi điện trở thành/không chắc chắn từ bên ngoài bối cảnh diễn viên có thể dẫn đến các vấn đề nghiêm trọng. – drexin

+0

@drexin, cảm ơn những người đứng đầu. Tôi đã sửa đổi câu trả lời của mình một chút. Tôi biết nó xấu xí đóng trên 'dep', nhưng nó giảm nhẹ bởi thực tế là không có gì nên được sử dụng' dep' cho đến khi nó chuyển sang trạng thái 'initialised' – cmbaxter

+0

Tuy nhiên, tôi sẽ không khuyên bạn nên làm điều đó. Công việc giải pháp được đề xuất của tôi và preStart đã được gọi là không đồng bộ. Móc 'preStart' có nghĩa là được sử dụng cho mã khởi tạo. Không cần phải bọc nó trong tương lai khác. – drexin

Các vấn đề liên quan