您隱問了兩個問題,所以讓我回答這兩個:
1.我怎樣才能讓我的AppEngine實例來處理多個併發請求?
你真的只需要做兩兩件事:
- 添加語句
<threadsafe>true</threadsafe>
您appengine-web.xml
文件,你可以在war\WEB-INF
文件夾中找到。
- 確保代碼中所有您的請求處理實際上是線程安全的,即只使用局部變量在
doGet(...)
,doPost(...)
等方法,或確保您同步所有訪問類或全局變量。
這將告訴AppEngine實例處理服務器框架,你的代碼是線程安全的,您允許它來調用所有的請求處理的多次在不同的線程來處理在同一時間幾個請求。注意:AFAIK,不可能根據每個servlet設置一個。所以,全部你的servlets需要是線程安全的!
因此,本質上,您發佈的執行程序代碼已包含在每個AppEngine實例的服務器代碼中,並且實際上從AppEngine創建(或重複使用)的單獨線程的run方法內調用您的doGet(...)
方法每個請求。基本上doGet()
已經是你的MyTask()
。
的文檔的相關部分是在這裏(雖然它並沒有真正多說):https://developers.google.com/appengine/docs/java/config/appconfig#Using_Concurrent_Requests
2.是否張貼代碼這個有用(或任何其他)目的是什麼?
AppEngine以其當前形式不允許您創建和使用自己的線程來接受請求。它不僅可以讓你創建線程內您doGet(...)
處理程序,使用你所提到的currentRequestThreadFactory()
方法,但只有這一個要求做並行處理,而不是接受平行的第二個(這種情況外doGet()
)。
名稱currentRequestThreadFactory()
在這裏可能有點誤導。這並不意味着它會返回RequestThreads
的current
Factory
,即處理請求的線程。這意味着它返回的Factory
可以在currentRequest
內創建Threads
。因此,不幸的是,實際上甚至不允許使用返回的ThreadFactory超出當前執行的範圍,就像您基於它創建Executor並將其保留在類變量中一樣。
對於前端實例,您在doGet()
調用中創建的任何線程將在您的doGet()
方法返回時立即終止。對於後端實例,您可以創建保持運行的線程,但由於您不允許打開服務器套接字來接受這些線程內的請求,所以這些仍然不允許您自己管理請求處理。
你可以找到你可以和不能的AppEngine上的servlet這裏裏面做更多的細節:
The Java Servlet Environment - The Sandbox(特別是線程部分)
爲了完整,讓我們來看看如何將您的代碼製作爲「合法」:
以下應該可以工作,但它不會在你的代碼能夠並行處理多個請求方面產生影響。這將完全由您在appengine-web.xml中的<threadsafe>true</threadsafe>
設置決定。所以,從技術上講,這段代碼實際上效率很低,並且在兩個線程之間分裂了基本上線性的程序流。但在這裏它是反正:
public class MyServlet implements HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
ThreadFactory threadFactory = ThreadManager.currentRequestThreadFactory();
Executor executor = Executors.newCachedThreadPool(threadFactory);
Future<MyResult> result = executor.submit(new MyTask(request)); // Fires off request handling in a separate thread
writeResponse(response, result.get()); // Waits for thread to complete and builds response. After that, doGet() returns
}
}
既然你已經是一個單獨的線程特定於當前正在處理請求裏面,你一定要救自己「線程內螺紋」的,只是這樣做,而不是:
public class MyServlet implements HttpServlet {
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
writeResponse(response, new MyTask(request).call()); // Delegate request handling to MyTask object in current thread and write out returned response
}
}
或者,甚至更好的辦法就是將代碼從MyTask.call()移到doGet()方法中。 ;)
除了 - 關於你提到的10個同時的servlet線程的限制:
這是一個(臨時)的設計決策,允許谷歌控制其服務器上的負載更容易(特別是內存使用servlet)。
你可以找到關於這些問題在這裏進行更多的討論:
此主題已被竊聽的挫折感了我,也因爲我我非常信任超精益servlet代碼,所以我通常的servlet可以輕鬆處理數百個(如果不是數千個)併發請求。由於每個實例有10個線程的任意限制,所以不得不支付更多的實例,這對我來說至少可以說是有點令人討厭。但通過閱讀上面張貼的鏈接,這聽起來像他們意識到這一點,並正在尋求更好的解決方案。那麼,讓我們來看看谷歌I/O 2013年將在五月帶來什麼公告... :)
+1 - 我真的很喜歡你正在考慮這個問題的方式以及你試圖解決問題的方式。但是,幸運的是,GAE不會以這種方式工作,並且'currentRequestThreadFactory()'方法根據您如何讀取它的名稱並不完全符合您的期望。我在下面發佈了一個答案,希望能夠清除方法名稱中的歧義。 (這是當前請求線程工廠,而不是當前請求線程工廠);) – 2013-02-23 07:45:56
只是出於好奇:有什麼具體的你試圖實現?我問,因爲有一段時間,我正在研究完全相同的問題。我希望能夠使用GAE實現某種長輪詢推送通知,但事實證明,還有很多其他問題會以這種方式得到解決。在允許您調整其安排和處理請求的方式方面,GAE確實非常有限。而其中的一些侷限性有點模糊......但是,當然,這就是它的速度和可擴展性。 – 2013-02-23 21:29:08