2013-07-22 37 views
8

Tôi nên xử lý ngoại lệ do DbActor đưa ra ở đây như thế nào? Tôi không chắc chắn làm thế nào để xử lý nó, nên ống trường hợp thất bại?Cách xử lý ngoại lệ với mẫu yêu cầu và giám sát

class RestActor extends Actor with ActorLogging { 
    import context.dispatcher 

    val dbActor = context.actorOf(Props[DbActor]) 
    implicit val timeout = Timeout(10 seconds) 


    override val supervisorStrategy: SupervisorStrategy = { 
    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) { 
     case x: Exception => ??? 
    } 
    } 

    def receive = { 
    case GetRequest(reqCtx, id) => { 

     // perform db ask 
     ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete { 
     case Success(obj) => { // some stuff } 
     case Failure(err) => err match { 
      case x: Exception => ??? 
     } 
     } 
    } 
    } 
} 

Sẽ rất vui khi nhận được suy nghĩ của bạn, cảm ơn trước!

Trả lời

6

Có một vài câu hỏi tôi có thể thấy ở đây dựa trên các câu hỏi trong mẫu mã của bạn:

  1. Tôi có thể làm gì loại của sự vật khi tôi ghi đè lên các hành vi giám sát mặc định trong định nghĩa của làm thế nào để xử lý ngoại lệ?

  2. Khi sử dụng ask, tôi có thể làm gì khi nhận được kết quả Failure trên số Future mà tôi đang đợi?

Hãy bắt đầu với câu hỏi đầu tiên trước (thường là ý tưởng hay). Khi bạn ghi đè chiến lược giám sát mặc định, bạn có khả năng thay đổi cách một số loại ngoại lệ không được giải quyết trong diễn viên con được xử lý liên quan đến việc phải làm gì với diễn viên con không thành công đó. Từ khóa trong câu trước đó là unhandled. Đối với các tác nhân đang thực hiện yêu cầu/phản hồi, bạn có thể thực sự muốn xử lý (bắt) các ngoại lệ cụ thể và trả về các kiểu phản hồi nhất định (hoặc thất bại tương lai ngược dòng, nhiều hơn về sau) thay vì để chúng không được giải quyết. Khi một ngoại lệ không được giải quyết xảy ra, về cơ bản bạn sẽ mất khả năng trả lời người gửi với mô tả về vấn đề và người gửi có thể sẽ nhận được TimeoutException thay vì Future của họ sẽ không bao giờ được hoàn thành. Khi bạn đã tìm ra những gì bạn xử lý một cách rõ ràng, bạn có thể xem xét tất cả các trường hợp ngoại lệ còn lại khi xác định chiến lược giám sát tùy chỉnh của bạn. Bên trong khối này ở đây:

OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) { 
    case x: Exception => ??? 
} 

Bạn nhận được một cơ hội để ánh xạ một loại ngoại lệ đối với một thất bại Directive, trong đó xác định như thế nào sự thất bại sẽ bị xử lý từ một quan điểm giám sát.Các tùy chọn là:

  1. Stop - Hoàn toàn ngăn chặn các diễn viên nhí và không gửi bất kỳ tin nhắn nhiều đến nó

  2. Chạy lại - Resume đứa trẻ thất bại, không khởi động lại vì thế nó giữ trạng thái nội bộ hiện tại của nó

  3. Restart - tương tự để tiếp tục, nhưng trong trường hợp này, ví dụ cũ phải ném ra ngoài một trường hợp mới được xây dựng và tình trạng nội bộ được thiết lập lại (preStart)

  4. leo thang - leo thang lên chuỗi để cha mẹ của người giám sát

Vì vậy, chúng ta hãy nói rằng đưa một SQLException bạn muốn tiếp tục và cho tất cả những người khác bạn muốn khởi động lại sau đó mã của bạn sẽ trông như thế này:

OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) { 
    case x: SQLException => Resume 
    case other => Restart 
} 

Bây giờ cho câu hỏi thứ hai liên quan đến việc cần làm khi bản thân số Future trả về phản hồi Failure. Trong trường hợp này, tôi đoán nó phụ thuộc vào những gì được cho là sẽ xảy ra do kết quả của Future. Nếu diễn viên còn lại tự chịu trách nhiệm hoàn thành các yêu cầu http (giả sử rằng httpCtx có chức năng complete(statusCode:Int, message:String) trên đó), sau đó bạn có thể làm một cái gì đó như thế này:

ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete { 
    case Success(obj) => reqCtx.complete(200, "All good!") 
    case Failure(err:TimeoutException) => reqCtx.complete(500, "Request timed out") 
    case Failure(ex) => reqCtx.complete(500, ex.getMessage) 
    } 

Bây giờ nếu diễn viên khác ở thượng nguồn chịu trách nhiệm hoàn thành yêu cầu http và bạn cần thiết để ứng phó với diễn viên đó, bạn có thể làm một cái gì đó như thế này:

val origin = sender 
    ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete { 
    case Success(obj) => origin ! someResponseObject 
    case Failure(ex) => origin ! Status.Failure(ex) 
    } 

phương pháp này giả định rằng trong khối thành công lần đầu tiên bạn muốn để xoa bóp các đối tượng kết quả trước khi trả lời. Nếu bạn không muốn làm điều đó và bạn muốn trì hoãn việc xử lý kết quả cho người gửi thì bạn chỉ có thể làm:

val origin = sender 
    val fut = ask(dbActor, ReadCommand(reqCtx, id)) 
    fut pipeTo origin 
+0

Tôi nghĩ rằng tôi nhận được nó, chìa khóa thực sự ở đây là để hiểu những gì là unhandled – graph1ZzLle

3

Có mẫu Activator (http://www.typesafe.com/activator) hiển thị ví dụ điển hình về việc buộc yêu cầu và giám sát cùng nhau: http://www.typesafe.com/activator/template/akka-supervision.

+0

cảm ơn bạn, tôi sẽ kiểm tra điều này ra – graph1ZzLle

+0

btw: ý chính của ví dụ đó là tác nhân mẹ giữ bản đồ người gửi và diễn viên công nhân ('Map [ActorRef, ActorRef]') để biết ai sẽ thông báo chiến lược giám sát khi một ngoại lệ trong đứa trẻ xảy ra. – rethab

0

Một hệ thống đơn giản hơn có thể muốn nắm bắt và chuyển tiếp tất cả các lỗi. Cho rằng tôi đã thực hiện chức năng nhỏ này để quấn nhận phương pháp, mà không làm phiền với sự giám sát:

import akka.actor.Actor.Receive 
    import akka.actor.ActorContext 
    /** 
    * Meant for wrapping the receive method with try/catch. 
    * A failed try will result in a reply to sender with the exception. 
    * @example 
    *   def receive:Receive = honestly { 
    *   case msg => sender ! riskyCalculation(msg) 
    *   } 
    *   ... 
    *   (honestActor ? "some message") onComplete { 
    *   case e:Throwable => ...process error 
    *   case r:_ => ...process result 
    *   } 
    * @param receive 
    * @return Actor.Receive 
    * 
    * @author Bijou Trouvaille 
    */ 
    def honestly(receive: =>Receive)(implicit context: ActorContext):Receive = { case msg => 
     try receive(msg) catch { case error:Throwable => context.sender ! error } 
    } 

sau đó bạn có thể đặt nó vào một tập tin gói và nhập khẩu a la akka.pattern.pipe và như vậy. Rõ ràng, điều này sẽ không giải quyết các trường hợp ngoại lệ được ném bởi mã không đồng bộ.