2017-06-22 69 views
0

我是一般玩框架和Scala的新手。雖然試圖測試和了解同步和異步操作之間的差異,使用下面的代碼:爲什麼我的異步控制器被阻塞?

package controllers 

import play.api.mvc._ 
import play.api.libs.concurrent.Execution.Implicits._ 
import scala.concurrent.Future 

object Application extends Controller { 

    def async = Action.async { 
    Logger.info("async start") 
    val resultF = Future { 
     Thread.sleep(2000) 
     Logger.info("async end") 
     Ok 
    } 
    Logger.info("non-blocking") 
    resultF 
    } 

    def sync = Action { 
    Thread.sleep(2000) 
    Ok 
    } 

} 

運行應用程序時,我已經在瀏覽器中的10個標籤請求「/異步」。我的期望是所有請求都需要大約2秒鐘的時間才能完成,我會在日誌10中看到「異步開始」條目,然後是10個「異步結束」條目。

但是,實際結果是看到「異步開始」,「異步結束」10次。直到前一個請求完成,下一個請求才開始。看起來異步的執行是阻塞的,根本無法處理併發請求。

我的問題是爲什麼系統會以這種方式運行,以及啓用併發請求處理的具體更改。

+0

https://www.playframework.com/documentation/2.5.x/ThreadPools – danielnixon

回答

0

該代碼工作正常。

使用ab(ApacheBench)或其他發送併發請求。

> ab -c 5 -n 5 localhost:9000/async 
This is ApacheBench, Version 2.3 <$Revision: 1757674 $> 
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 
Licensed to The Apache Software Foundation, http://www.apache.org/ 

Benchmarking localhost (be patient).....done 


Server Software:   
Server Hostname:  localhost 
Server Port:   9000 

Document Path:   /async 
Document Length:  0 bytes 

Concurrency Level:  5 
Time taken for tests: 2.013 seconds 
Complete requests:  5 
Failed requests:  0 
Total transferred:  375 bytes 
HTML transferred:  0 bytes 
Requests per second: 2.48 [#/sec] (mean) 
Time per request:  2013.217 [ms] (mean) 
Time per request:  402.643 [ms] (mean, across all concurrent requests) 
Transfer rate:   0.18 [Kbytes/sec] received 

Connection Times (ms) 
       min mean[+/-sd] median max 
Connect:  0 0 0.0  0  0 
Processing: 2008 2011 2.0 2011 2013 
Waiting:  2007 2011 2.0 2011 2013 
Total:  2008 2011 2.0 2011 2013 

Percentage of the requests served within a certain time (ms) 
    50% 2011 
    66% 2012 
    75% 2012 
    80% 2013 
    90% 2013 
    95% 2013 
    98% 2013 
    99% 2013 
100% 2013 (longest request) 

我送5個併發請求,並全部結束預期(見上文Time taken for tests: 2.013 seconds

+0

感謝您提供ApacheBench。我使用了你的測試,並確認它適用於我: 這個問題看起來實際上是瀏覽器,當對相同的URL進行(GET)請求時,Chrome鎖定緩存並阻止後續請求,請參閱此問題: [ Chrome在向同一資源發出多個請求時停頓?](https://stackoverflow.com/questions/27513994/chrome-stalls-when-making-multiple-requests-to-same-resource) –

2

使用Action.async並不意味着你不堵。這完全取決於你是否使用阻塞API。

Thread.sleep是阻塞操作在Future但你不能用信號通知你正在做這樣ExecutionContext,這樣的行爲會因您ExecutionContext使用什麼和多少個處理器機器有改變。您的代碼與預期一致ExecutionContext.global

在這兩種情況下,您正在使用Thread.sleep(2000)阻止線程。

在這兩種情況下,睡眠呼叫都發生在動作的線程池中(這不是最優的)。

如瞭解播放線程池說:

遊戲框架,從下往上,異步Web框架。流是使用迭代異步處理的。 Play中的線程池被調優爲使用比傳統Web框架更少的線程,因爲play-core中的IO永遠不會阻塞。因此,如果您打算編寫阻止IO代碼或可能執行大量CPU密集型工作的代碼,則您需要確切知道哪個線程池承載了該工作負載,並且您需要相應地調整它。

你的情況,你只是在等待在這兩種情況下幾秒鐘,其阻斷線程,其並行因素的默認設置爲1

如果您阻止的線程,你可以使用這樣的事情:

def async = Action.async { 
    Logger.info("async start") 
    val resultF = Future { 
     blocking{ 
     Thread.sleep(2000) 
     Logger.info("async end") 
     Ok 
     } 
    } 
    Logger.info("non-blocking") 
    resultF 
    }