2013-07-31 61 views
3

我已經編寫了一個應用程序(Scala)Play,當我在本地機器上運行它時,它的工作原理與我預期的完全相同。我正在處理的方法(可能有其他問題,我沒有廣泛檢查)是由POST請求觸發的方法。它返回一個Async對象,因爲該方法完成的評估返回一個Future。(Scala)中的異步請求播放「掛起」

但是,當我部署它時,這種方法的行爲非常不同。我一整天都在追逐這件事,但我已經到了無法解釋發生什麼事情的地步。

的方法看起來是這樣的:

def add = Action(parse.json) { request => 
    Async { 
     val req = request.body.asOpt[TaskRequest] map { r => 
     val job = createJob(r) 
     submitJob(job, r) 
     job map { ij => 
      allowOrigin(Created(ij).withHeaders("Location" -> routes.Jobs.read(ij._id.stringify).url)); 
     } 
     } 
     req.get 
    } 
    } 

總體而言,這是非常簡單的。 createJob方法返回Future[Job]。撥打submitJob是一種「隨時隨地丟失」的電話來觸發一些背景資料(我不想等待)。然後,我處理job結果,以產生Result對象,當job已被創建時

所有這些都像我在本地機器上描述的那樣工作。但是當我部署這個時候,真的很奇怪。那個submitJob調用會觸發長時間運行的計算。正如我所說,我沒有興趣等待它,這就是爲什麼我甚至不使用Future,它返回任何地方。

但是,這是奇怪的部分...在我的開發機器上,我的POST請求(直接調用add)的響應在創建作業後立即返回。但是在部署機器上,我只在submitJob任務完成後纔得到響應!換句話說,我得到這個req作爲Future,看起來與那個submitJob調用(它當然不取決於它的結果)完全無關,但在部署環境中,Async塊將不會返回,直到submitJob完成(即使req幾乎立即履行)。

這是怎麼回事?我已將println聲明全部通過代碼。它幾乎立即通過req.get電話。但整體(HTTP)響應肯定會等到submitJob完成。 Async莫名其妙地知道submitJob電話?如果是這樣,爲什麼它在兩臺不同的機器上表現不同?

任何想法?

+0

部署機器是否在您的控制之下?你能從兩臺機器獲得堆棧轉儲並進行比較嗎? – huynhjl

+0

我不確定我明白你的意思。這裏沒有堆棧跟蹤,因爲沒有異常。如果我想嘗試轉儲堆棧(沒有例外),我不知道我該怎麼做,或者在哪裏? –

+0

抱歉,我的意思是一個線程轉儲。如果你下載了一個像http://visualvm.java.net/這樣的工具,或者通過向Windows中的進程或CTRL-BREAK發送SIGQUIT來創建線程轉儲,那麼你可以看到JVM中所有線程的堆棧調用。找出根本原因很有幫助。 – huynhjl

回答

4

好的,我想通了。哦,那是多麼的痛苦。問題在於我的「火和忘記」任務是一項長期運行的任務。但是部署機器上的線程池大小最終比我的開發機器小。結果,這個長時間運行的任務在線程池中丟失並堵塞了它。所以AsyncResult的處理可能被長時間運行的計算所取代(它實際上是由於長時間運行計算而導致的結果,但最終結果是它被阻塞了)。

Heather Miller在Scala 2.10上閱讀了一篇關於期貨的非常好的解釋之後,我注意到blocking這個構造可以用來臨時擴大單一線程池的大小。我用一個blocking調用封裝了我的阻止代碼,這就實現了。據推測,這給了我一個額外的臨時工來處理我的阻斷電話,結果,這些事情得到了及時的處理。

(注意,包裹submitJob電話是無用的,因爲這本身並沒有阻攔。我所要做的就是進入submitJob呼叫並找到實際的阻塞是地方和換行。)

3

雖然你已經想通了,但還有另一種選擇。這通常發生在使用scala的默認ThreadPool(即scala.concurrent.ExecutionContext.Implicits.global)時。它使用機器的尺寸爲number of coresnewFixedSizeThreadPool

另一種方法是在上面的未來呼叫中使用您自己的ExecutionContext。即在上面的代碼中,加入這一行:那麼

implicit val exec = ExecutionContext.fromExecutor(Executors.newCachedThreadPool()) 

你所有的future通話將利用上述ExecutionContext

+0

非常好的一點。我可以在這裏理解所有的「隱含的」巫術,因爲每次你代表未來時必須明確地說明這些東西是一種痛苦。但是,知道如何明確定義一個替代的隱式上下文也是很好的。謝謝。 –