Play Framework2.1的基本設計思想是可以快速處理大量耗時較少的請求,比較耗時的請求採用異步方式完成。爲了很好地說明這一點,讓咱們來看一個例子,編寫控制器代碼以下: java
public static AtomicInteger count = new AtomicInteger(0); public static Result test(Long id) { if(id!=0){ try { System.out.println("sleeping...:"+count.addAndGet(1)); Thread.currentThread().sleep(1000000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ System.out.println("no sleep"); } return ok("good."); }在conf/routes文件中添加以下路由:
GET /:id controllers.Application.test(id:Long)執行play run啓動項目,下面咱們打開瀏覽器進行測試。測試地址以下:
http://localhost:9000/1 - http://localhost:9000/9
須要注意的是,全部的請求須要在瀏覽器的一個窗口中完成,具體緣由請見下面的【說明】。控制檯消息以下: 數據庫
能夠看出,在咱們發送第9次請求時,服務器報了error,錯誤緣由是「AskTimeoutException」,請求actor超時。 瀏覽器
【說明】 服務器
在上面的測試中,要求全部請求須要在一個瀏覽器窗口中完成,主要是由於各個版本的瀏覽器針對同一個域,有最大鏈接數限制,例如IE六、IE8和Chrome21的鏈接數以下: 架構
Chrome21的最大鏈接數:6 IE8的最大鏈接數:6 IE6的最大鏈接數:2這意味在訪問下一個頁面時,須要將以前的頁面關掉,不然在Chrome21中,當打開第7個選項卡訪問頁面時,前面6個選項卡Chrome提示「正在等待響應」, 而第7個選項卡Chrome提示「正在發送請求」,這是由於前面的6個選項卡已經佔滿了6個鏈接,第7個選項卡只能等待前面的鏈接釋放。
從上面的實驗結果,能夠觀察到,默認狀況下Play2.1只能同時處理8個耗時請求,在這個8個耗時請求未結束以前,第9個請求將會在默認的等待時間(1秒)結束後,報」500服務器內部錯誤「。 併發
須要說明的是,Play2.1的默認配置已經可以知足大部分小型應用的須要了。但在面對數據/計算密集型的應用,或是高併發的應用,默認的配置就顯的力不從心了。本文主要從兩方面來提升Play2.1的性能,一方面是提升請求處理的併發數;另外一方面,僅僅提升處理請求的併發數,在高併發狀況下(如壓力測試)仍然會處理「AskTimeoutException」,因此要提升這個等待時間。 app
在個人上一篇文章《Play Framework2.1源碼分析 - 架構設計及線程策略分析》介紹了,在Play2.x中,實際處理請求的執行環境是AKKA的actors,而執行actors的線程資源是由跟actor相關聯的dispatcher管理的。在Play2.1中,全部的AKKA actors都使用默認的default-dispatcher,其默認配置以下: 異步
play { akka { actor { retrieveBodyParserTimeout = 1 second default-dispatcher = { fork-join-executor { parallelism-min = 8 parallelism-factor = 1.0 parallelism-max = 24 } } } } }
其中retrieveBodyParserTimeout參數值的是,若是沒有可用的actor處理請求,則默認等待1s,若是尚未則報500錯誤。接下來的三個參數parallelism-min、parallelism-factor和parallelism-max,就是具體的線程池配置了。parallelism-min和parallelism-max參數指明最小和最大線程數分別是8和24,parallelism-factor是線程池大小的計算因子。 看到min和max,相信不少人第一時間會聯想到數據庫鏈接池的配置,須要注意的是,這裏的min和max的含義和數據庫鏈接池的含義徹底不一樣,只是做爲最終計算結果的一個參考比較。下面說明一下線程池大小計算的具體過程: 高併發
- 首先計算parallelism-factor*processors, 其中processors爲CPU的總核數,例如對於i5處理器,processors大小爲4 源碼分析
- 若是parallelism-factor*processors計算結果小於parallelism-min,則線程池大小爲parallelism-min
- 若是parallelism-factor*processors計算結果大於parallelism-max,則線程池大小爲parallelism-max
咱們看到,parallelism-min和parallelism-max參數只是起到校訂parallelism-factor*processors計算結果的做用。
好了,經過上面的介紹,我想你應該知道怎麼作了,這裏給一個示例,把下面這部分配置追加到con/application.conf文件的尾部。下面的參數書寫方式和自動生成的不太同樣,不用擔憂,Play支持多種書寫方式,例如點式「db.default.user=sa」和下面這種相似JSON的方式,具體請參考官方文檔,
play { akka { actor { retrieveBodyParserTimeout = 5 second default-dispatcher = { fork-join-executor { parallelism-min = 10 parallelism-factor = 100 parallelism-max = 100 } } } } }