Handling asynchronous results【翻譯】

原文:Handling asynchronous results

讓控制器變成異步的

本質(zhì)上,Play Framework從里到外都是異步的。Play以異步,非阻塞方式處理每一個(gè)請(qǐng)求。

默認(rèn)配置已經(jīng)為異步控制器做了優(yōu)化。也就是說,在控制器中,應(yīng)用代碼應(yīng)該避免阻塞,例如,讓控制器代碼等待一個(gè)操作。這種常見的阻塞操作的例子有JDBC調(diào)用,流式API,HTTP請(qǐng)求和長時(shí)間的計(jì)算。

盡管在默認(rèn)的執(zhí)上下文中可以增加線程的數(shù)量,讓更多并發(fā)請(qǐng)求處在阻塞中的控制器執(zhí)行,但是按照保持控制器異步的推薦方法,可以讓線程數(shù)據(jù)更容易調(diào)整并在負(fù)載下保證系統(tǒng)的響應(yīng)。

創(chuàng)建非阻塞Action

由于Play的工作方式,Action代碼執(zhí)行必須盡可能的快,例如,非阻塞。因此,如果我們還不能生產(chǎn)結(jié)果,那么我們應(yīng)該返回什么作為結(jié)果呢?答案是一個(gè)future 結(jié)果!

Future[Result]最終將被用 Result類型的值回填。通過給定的 Future[Result] 代替正常的結(jié)果,我可以很快的生成結(jié)果而不阻塞。一旦執(zhí)行完成,Play就會(huì)返回結(jié)果。

當(dāng)?shù)却龖?yīng)答時(shí),網(wǎng)絡(luò)客戶端將被阻塞,但是在服務(wù)端沒有什么會(huì)被阻塞,服務(wù)端的資源,可以被用來服務(wù)其他客戶端。

怎樣創(chuàng)建Future[Result]

為了創(chuàng)建 Future[Result] ,首先我們還需要一個(gè)Future,這個(gè)Future將會(huì)給我們一個(gè)需要計(jì)算結(jié)果的真實(shí)值:

import play.api.libs.concurrent.Execution.Implicits.defaultContext

val futurePIValue: Future[Double] = computePIAsynchronously()
val futureResult: Future[Result] = futurePIValue.map { pi =>
Ok("PI value computed: " + pi)
}

Play所有的異步API調(diào)用,都會(huì)返回給你一個(gè)Future。無論你是使用play.api.libs.WS API調(diào)用一個(gè)外部的Web服務(wù),還是使用Akka 調(diào)度一個(gè)異步的任務(wù)或使用play.api.libs.Akka和Actor通信,都是這樣。 這是異步執(zhí)行的一段代碼并得到Future的簡(jiǎn)單方式:

val futureInt: Future[Int] = scala.concurrent.Future {
intensiveComputation()
}

注意:重要的是理解哪個(gè)線程代碼運(yùn)行返回Future。在上面的兩段代碼塊中,導(dǎo)入了Play默認(rèn)的執(zhí)行環(huán)境。這是一個(gè)隱式的參數(shù),這個(gè)參數(shù)傳遞到所有Future API上接受回調(diào)的方法。盡管不一定,但是多數(shù)情況下執(zhí)行上下文相當(dāng)于線程池。

你不可能只通過在Future中封裝就魔術(shù)般的把同步線程轉(zhuǎn)成異步的。如果你不能改變應(yīng)用的架構(gòu)來避免阻塞操作,在某一時(shí)刻操作就一定會(huì)被執(zhí)行,然后線程將會(huì)阻塞。 因此為了在Future中封裝操作,有必要配置它,讓它運(yùn)行到一個(gè)獨(dú)立的執(zhí)行上下文中,這個(gè)上下文配置了足夠多的線程來運(yùn)行預(yù)期的并發(fā)。詳見 理解Play線程池

使用Actor處理阻塞操作也是有用的。Actor為處理超時(shí)和失敗提供了一個(gè)清理模式,設(shè)置阻塞執(zhí)行上下文,并管理任何可能與服務(wù)相關(guān)的狀態(tài)。Actor還提供了類似ScatterGatherFirstCompletedRouter 的模式在一組后端服務(wù)器上同時(shí)處理緩存和數(shù)據(jù)庫請(qǐng)求并允許遠(yuǎn)程執(zhí)行。但是Actor 也許過度的依賴你需要的。

返回Future

到目前為止,雖然我們使用Action.apply構(gòu)建方法構(gòu)建Action,但是我們需要使用 Action.async 構(gòu)建方法來發(fā)送一個(gè)異步的結(jié)果:

import play.api.libs.concurrent.Execution.Implicits.defaultContext

def index = Action.async {
val futureInt = scala.concurrent.Future { intensiveComputation() }
futureInt.map(i => Ok("Got result: " + i))
}

Action默認(rèn)是異步的

默認(rèn)情況下,Play的Action是異步的。例如,在下面的控制器代碼中,代碼的{ Ok(...) } 部分不是控制器的方法體。它是一個(gè)被傳遞到Action對(duì)象的apply 方法的匿名函數(shù),它會(huì)創(chuàng)建一個(gè)Action類型的對(duì)象。本質(zhì)上,你寫的匿名函數(shù)將會(huì)被調(diào)用,并且它的結(jié)果會(huì)被封裝到 Future。

def echo = Action { request =>
Ok("Got request [" + request + "]")
}

注意:Action.apply 和Action.async 都創(chuàng)建Action對(duì)象,它們的本質(zhì)是相同的。有一種單一的Action,它是異步的,不是兩種(一種是異步的,一種是同步的)。 .async 構(gòu)建器只是一個(gè)基于返回Future的API,簡(jiǎn)化創(chuàng)建Action的工具,它可以讓開發(fā)更容易寫非阻塞的代碼。

處理超時(shí)

通常為了避免如果有什么錯(cuò)誤而導(dǎo)致的Web瀏覽器阻塞并等待, 適當(dāng)?shù)奶幚沓瑫r(shí)很有用。你可以使用 play.api.libs.concurrent.Timeout 在非阻塞超時(shí)中封裝Future :

import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
import play.api.libs.concurrent.Timeout

def index = Action.async {
Timeout.timeout(actorSystem, 1.seconds) {
intensiveComputation().map { i =>
Ok("Got result: " + i)
}
}.recover {
case e: TimeoutException =>
InternalServerError("timeout")
}
}

注意:超時(shí)不同于取消——即使在超時(shí)的情況下,給定的Future仍將完成,哪怕完成的值沒有返回。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容