您隐问了两个问题,所以让我回答这两个:
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