Java 处理并发用户的多线程GAE servlet
我希望对我的GAE servlet进行多线程处理,以便同一实例上的同一servlet可以同时处理来自不同用户的多达10个并发请求(在前端实例上,我相信最大线程数是10个),在每个用户之间进行时间分配Java 处理并发用户的多线程GAE servlet,java,multithreading,google-app-engine,servlets,Java,Multithreading,Google App Engine,Servlets,我希望对我的GAE servlet进行多线程处理,以便同一实例上的同一servlet可以同时处理来自不同用户的多达10个并发请求(在前端实例上,我相信最大线程数是10个),在每个用户之间进行时间分配 public class MyServlet implements HttpServlet { private Executor executor; @Override public void doGet(HttpServletRequest request, HttpSe
public class MyServlet implements HttpServlet {
private Executor executor;
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
if(executor == null) {
ThreadFactory threadFactory = ThreadManager.currentRequestFactory();
executor = Executors.newCachedThreadPoolthreadFactory);
}
MyResult result = executor.submit(new MyTask(request));
writeResponseAndReturn(response, result);
}
}
所以基本上,当GAE启动时,当它第一次收到对这个servlet的请求时,就会创建一个执行器,然后保存它。然后,每个新的servlet请求都使用该执行器生成一个新线程。显然,MyTask
中的所有内容都必须是线程安全的
我关心的是这是否真的像我希望的那样。也就是说,这段代码是否创建了一个能够同时处理来自多个用户的多个请求的非阻塞servlet?如果没有,为什么我需要做什么来修复它?总的来说,GAE大师还能发现什么是完全错误的吗?提前谢谢。我认为您的代码不起作用
doGet
方法正在servlet容器管理的线程中运行。当一个请求进入时,一个servlet线程被占用,直到doGet
method返回,它才会被释放。在您的代码中,executor.submit
将返回一个Future
对象。要获得实际结果,您需要在Future
对象上调用get
方法,它将一直阻塞,直到MyTask
完成其任务。只有在这之后,doGet
方法返回,新的请求才能启动
我不熟悉GAE,但根据,您可以将servlet声明为线程安全的,然后容器将并行地向每个web服务器发送多个请求:
<!-- in appengine-web.xml -->
<threadsafe>true</threadsafe>
真的
您含蓄地问了两个问题,所以让我回答这两个问题:
1。如何让AppEngine实例处理多个并发请求?
你只需要做两件事:
true
添加到appengine web.xml
文件中,您可以在war\web-INF
文件夹中找到该文件doGet(…)
、doPost(…)
等方法中仅使用局部变量,或者确保同步对类或全局变量的所有访问doGet(…)
方法。基本上,doGet()
已经是您的MyTask()
文档的相关部分在这里(尽管它并没有说太多):
2。张贴的代码是否对此(或任何其他)用途有用?
AppEngine的当前形式不允许您创建和使用自己的线程来接受请求。它只允许您使用您提到的currentRequestThreadFactory()
方法在doGet(…)
处理程序中创建线程,但只允许对这一个请求执行并行处理,而不允许并行接受第二个请求(这发生在doGet()
之外)
名称currentRequestThreadFactory()
在这里可能有点误导。这并不意味着它将返回请求线程
的当前
工厂,即处理请求的线程。这意味着它返回一个工厂
,该工厂可以在当前请求
中创建线程
。因此,不幸的是,实际上甚至不允许在当前doGet()
执行的范围之外使用返回的ThreadFactory,就像您建议的那样,基于它创建一个执行器并将其保留在类变量中
对于前端实例,当doGet()
方法返回时,在doGet()调用中创建的任何线程都将立即终止。对于后端实例,您可以创建保持运行的线程,但由于不允许打开服务器套接字以接受这些线程内的请求,因此这些线程仍不允许您自己管理请求处理
您可以在appengine servlet中找到关于您可以做什么和不可以做什么的更多详细信息:
(特别是螺纹部分)
为了完整性,让我们看看如何使您的代码“合法”:
下面的代码应该可以工作,但是对于代码能够并行处理多个请求来说,这并没有什么区别。这将完全由appengine-web.xml中的true
设置决定。所以,从技术上讲,这段代码效率很低,它将一个基本上是线性的程序流分割到两个线程上。但无论如何,这就是:
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
}
}
或者,更好的方法是将代码从MyTask.call()移到doGet()方法中
放在一边-关于您提到的10个并发servlet线程的限制:
这是一个(临时)设计决策,允许谷歌更轻松地控制服务器上的负载(特别是servlet的内存使用)
您可以在此处找到有关这些问题的更多讨论:
这个话题也一直困扰着我,因为我非常相信超精简的servlet代码,所以我通常的servlet可以轻松地处理数百个(如果不是数千个)的servlet
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
}
}
private static Executor executor;