2013-07-22 39 views
8

我應該如何處理由DbActor在這裏引發的異常?我不知道如何處理它,應該管理失敗案例?如何處理異常請求模式和監督

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 => ??? 
     } 
     } 
    } 
    } 
} 

很高興能得到你的思想,在此先感謝!

回答

6

有一對夫婦的問題,我可以根據您的代碼示例中的問題,在這裏看到:

  1. 我能做些什麼類型的東西,當我覆蓋的定義默認管理程序行爲如何處理異常?

  2. 當使用ask時,當我在等待的Future上獲得Failure結果時,我可以執行哪些類型的操作?

讓我們先從第一個問題第一個(通常是一個好主意)。當您重寫默認主管策略時,您可以更改處理子主角中某些未處理的異常類型的方式,以處理如何處理該失敗的子主角。前一句中的關鍵詞是unhandled。對於正在做請求/響應的角色,您可能實際上想要處理(捕獲)特定的異常,並返回某些響應類型(或者上游未來失敗,稍後再處理),而不是讓它們不處理。當發生未處理的異常時,您基本上無法通過問題描述來回復發件人,發件人可能會收到TimeoutException,因爲他們的Future將永遠不會完成。一旦明確了你明確處理的內容,那麼在定義自定義主管策略時,可以考慮所有其他異常。這裏面塊這裏:

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

你得到一個機會,一個異常類型映射到故障Directive,它定義瞭如何將故障從監管的角度來處理。選項有:

  1. 停止 - 完全停止兒童演員和不發送任何消息給它

  2. 恢復 - 恢復失敗的孩子,而不是重新啓動從而保持其當前的內部狀態

  3. 重啓 - 類似恢復,但在這種情況下,舊的實例是扔掉一個新的實例構造和內部狀態復位(啓動前)

  4. 升級 - 上報了連鎖經營,向主管的父

所以我們可以說,鑑於一個SQLException你想恢復並給予你要重新啓動,然後其他所有的代碼應該是這樣的:

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

現在第二個問題涉及當Future本身返回Failure響應時該怎麼做。在這種情況下,我想這取決於Future的結果應該發生的情況。如果剩下的演員本身是負責完成HTTP請求(讓我們說,httpCtx上有一個complete(statusCode:Int, message:String)功能),那麼你可以做這樣的事情:

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) 
    } 

現在,如果其他演員的上游是負責完成HTTP請求和你需要給演員的迴應,你可以做這樣的事情:

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

這種方法假定在成功塊,你首先要響應之前按摩結果對象。如果你不想這樣做,你想推遲處理結果向發送者,那麼你可能只是這樣做:

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

我想我明白了,這裏真正的關鍵是理解什麼是未處理的 – graph1ZzLle

0

對於比較簡單的系統,一個可能要趕上並轉發所有的錯誤。對於我做了這個小功能包接收方法,而不與監督困擾:

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 } 
    } 

然後你可以將其放入包文件並導入拉akka.pattern.pipe和這樣。顯然,這不會處理異步代碼拋出的異常。