2014-03-12 70 views
1

我正在做一些使用噴霧的Http請求處理。對於一個請求,我啓動一個演員,並將有效載荷發送給演員進行處理,在演員完成有效載荷的工作後,我會對該演員致電context.stop(self)來演員下臺。這個想法是爲了防止演員過飽和物理機器。從噴霧路線產生的演員困惑

這是我有事情設置..

httphandler.scala,我已成立的路線如下:

path("users"){ 
    get{ 
     requestContext => { 
     val userWorker = actorRefFactory.actorOf(Props(new UserWorker(userservice,requestContext))) 
     userWorker ! getusers //get user is a case object 
     } 
    } 
} ~ path("users"){ 
    post{ 
     entity(as[UserInfo]){ 
      requestContext => { 
      userInfo => { 
      val userWorker = actorRefFactory.actorOf(Props(new UserWorker(userservice,requestContext))) 
      userWorker ! userInfo 
      } 
     } 
     } 
    } 
} 

UserWorker演員的定義如下:

trait RouteServiceActor extends Actor{ 
    implicit val system = context.system 
    import system.dispatcher 
    def processRequest[responseModel:ToResponseMarshaller](requestContex:RequestContext)(processFunc: => responseModel):Unit = { 
     Future{ 
     processFunc 
     } onComplete { 
      case Success(result) => { 
      requestContext.complete(result) 
      } 
      case Failure(error) => requestContext.complete(error) 
     } 
    } 
} 

class UserWorker(userservice: UserServiceComponent#UserService,requestContext:RequestContext) extends RouteServiceActor{ 
def receive = { 
    case getusers => processRequest(requestContext){ 
     userservice.getAllUsers 
    } 
    context.stop(self) 
    } 
    case userInfo:UserInfo => { 
    processRequest(requestContext){ 
     userservice.createUser(userInfo) 
    } 
    context.stop(self) 
} 

} 

我的第一個問題是,我是否以真正的異步方式處理請求?我的代碼有哪些缺陷?

我的第二個問題是requestContext.complete是如何工作的?由於原始請求處理線程不再存在,因此requestContext如何將計算結果發送回客戶端。

我的第三個問題是,因爲我在每個部分方法之後調用context.stop(self),是否有可能在處理不同消息的過程中終止工作人員。

我的意思是,雖然演員收到一條消息來處理getusers,但同一個演員完成了UserInfo的處理並終止了演員,然後才能到達「getusers」消息。我在每次請求時都會創建新的演員,但是有可能在封面下,actorRefFactory提供了一個以前創建的演員的參考,而不是一個新演員?

我對所有的抽象都感到困惑,如果有人能爲我分解它,那將會很棒。

謝謝

+1

除了原來的問題上@jrudolph已經回答了:如果你想返回從演員到HTTP用戶的一些結果,那麼你應該使用阿卡問模式: '(得到與路徑( 「用戶」)){ \t完整{(?userWorker getusers).mapTo [PutResultTypeHere]} }' 並在您的UserWorker中: 'case getusers => sender! processRequest(...)' –

回答

12

1)請求是否異步處理?是的。但是,如果您立即將實際處理委託給未來,則您對每個請求演員的收益不會太高。在這個簡單的情況下,更清潔的方式是寫你的路線,就像

path("users") { 
    get { 
     complete(getUsers()) 
    } 
} 

def getUsers(): Future[Users] = // ... invoke userservice 

每請求參與者更有意義,如果你也想在平行航路處理邏輯運行,或者如果處理要求有更復雜的要求,例如如果您需要從多個服務並行查詢事務,或者需要保持每個請求狀態,而某些後臺服務正在處理請求。有關此一般主題的一些信息,請參閱https://github.com/NET-A-PORTER/spray-actor-per-request

2)requestContext.complete如何工作?在幕後,它將HTTP響應作爲普通的演員消息「tell」發送給噴灑式HTTP連接actor。因此,基本上RequestContext只是將ActorRef包裝爲可以安全使用的HTTP連接。

3)是否有可能「工人」被context.stop(self)終止?我認爲在幕後制定計劃有些困惑。當然,你正在終止使用context.stop的演員,但這隻會阻止演員而不是任何線程(因爲線程完全獨​​立於Akka中的演員實例進行管理)。由於你沒有真正利用演員的優勢,即封裝並同步對可變狀態的訪問,一切都應該工作(但如1所述)對於這種用例來說是不必要的複雜)。 akka文檔提供了大量有關演員,期貨,調度員和ExecutionContext如何協同工作的信息。

+0

感謝您的所有信息。這非常有幫助 –

2

除了jrudolph回答你的噴霧路由結構甚至不應該編譯,導致你的後分支你沒有明確指定requestContext。這種結構可以簡化一個位,以這樣的:

def spawnWorker(implicit ctx: RequestContext): ActorRef = { 
    actorRefFactory actorOf Props(new UserWorker(userservice, ctx)) 
} 

lazy val route: Route = { 
    path("users") { implicit ctx => 
    get { 
     spawnWorker ! getUsers 
    } ~ 
    (post & entity(as[UserInfo])) { 
     info => spawnWorker ! info 
    } 
    } 
} 

info => spawnWorker ! info也可以簡化到spawnWorker ! _

還有一個重要點有關明確的ctx聲明和complete指示。如果你明確你的路線,你不能用完整的指令宣佈ctx,你必須明確地寫ctx.complete(...)link在這個問題上

+0

@Alexlv - 我在HttpService中混合了我定義路線的特質。不應該在HttpService特性中隱式定義requestContext嗎? –

+0

@sc_ray這是爲什麼?看一看HttService代碼,如果requestContext只能從請求本身獲得,那麼它是如何實現的。實際上,哪些指令適用於,請查看Directive.apply方法'apply [L <:Hlist](r:L => Route):Route',其中'Route'是RequestContext => Unit'函數。 – 4lex1v

+0

@Alexlv - 因爲我的代碼並編譯和請求處理正在做它是desired.Maybe隱在我的包進口的一個定義方式這很有趣。 –