多线程GAE servlet处理并发用户

IAm*_*aja 9 java google-app-engine multithreading servlets

我想多线程化我的GAE servlet,以便同一个实例上的同一个servlet可以同时处理多达10个(在前端实例上我认为 max#threads为10)来自不同用户的并发请求,每个之间的时间间隔为他们.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以基本上当GAE启动时,它第一次获得对这个servlet的请求时,Executor会创建一个然后保存.然后每个新的servlet请求使用该执行器来生成一个新线程.显然,里面的一切都MyTask必须是线程安全的.

我关心的是这是否真正做到了我希望它做的事情.也就是说,此代码是否创建了一个非阻塞的servlet,可以同时处理来自多个用户的多个请求?如果没有,为什么以及我需要做些什么来解决它?而且,一般来说,GAE大师可以发现其他任何错误吗?提前致谢.

eri*_*son 6

我不认为你的代码会起作用.

doGet方法在由servlet容器管理的线程中运行.当请求进入时,servlet线程被占用,并且在doGet方法返回之前不会释放它.在您的代码中,executor.submit将返回一个Future对象.要获得实际结果,您需要getFuture对象上调用方法,并且它将阻塞直到MyTask完成其任务.只有在那之后,doGet方法才会返回并且新请求可以启动.

我不熟悉GAE,但根据他们的文档,您可以将您的servlet声明为线程安全,然后容器将并行地向每个Web服务器分派多个请求:

<!-- in appengine-web.xml -->
<threadsafe>true</threadsafe>
Run Code Online (Sandbox Code Playgroud)


Mar*_* A. 5

你含蓄地问了两个问题,让我回答两个问题:

1.如何让我的AppEngine实例处理多个并发请求?

你真的只需要做两件事:

  1. 将语句添加<threadsafe>true</threadsafe>到您appengine-web.xml可以在war\WEB-INF文件夹中找到的文件中.
  2. 确保里面的代码,所有的请求处理实际上是线程安全的,即只使用局部变量在doGet(...),doPost(...)等方法,或确保您同步所有访问类或全局变量.

这将告诉AppEngine实例服务器框架您的代码是线程安全的,并且允许它在不同的线程中多次调用所有请求处理程序以同时处理多个请求.注意:AFAIK,无法基于每个servlet设置此值.所以,你的所有 servlet都需要是线程安全的!

因此,实质上,您发布的执行程序代码已经包含在每个AppEngine实例的服务器代码中,并且实际上是doGet(...)从AppEngine为每个请求创建(或重用)的单独线程的run方法内部调用您的方法.基本上doGet()已经你的了MyTask().

文档的相关部分在这里(虽然它并没有多说):https://developers.google.com/appengine/docs/java/config/appconfig#Using_Concurrent_Requests

2.发布的代码对此(或任何其他)目的有用吗?

当前形式的AppEngine不允许您创建和使用自己的线程来接受请求.它只允许你使用你提到的方法你的doGet(...)处理程序中创建线程currentRequestThreadFactory(),但只对这一个请求进行并行处理而不是并行接受第二个(这发生在外面 doGet()).

这个名字currentRequestThreadFactory()可能会有点误导.这并不意味着它会返回current FactoryRequestThreads,即处理请求的线程.这意味着它返回一个Factory可以Threads在内部创建的内容currentRequest.所以,遗憾的是,实际上甚至不允许在当前doGet()执行范围之外使用返回的ThreadFactory ,就像你建议通过创建一个基于它的Executor并将其保存在类变量中一样.

对于前端实例,您在方法返回doGet()时立即终止在调用中创建的任何线程doGet().对于后端实例,您可以创建继续运行的线程,但由于您不允许打开服务器套接字来接受这些线程内的请求,因此这些仍然不允许您自己管理请求处理.

你可以找到你还有什么细节可以不能在这里的AppEngine上的servlet的内部做:

Java Servlet环境 - 沙箱(特别是" 线程"部分)

为了完整起见,让我们看看您的代码如何变得"合法":

以下内容应该可行,但在代码能够并行处理多个请求方面不会产生影响.这将仅由<threadsafe>true</threadsafe>您的appengine-web.xml中的设置决定.因此,从技术上讲,这段代码实际上效率很低,并且在两个线程之间划分了基本上线性的程序流.但无论如何它在这里:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

因为你已经在一个特定于你当前正在处理的请求的单独线程中,所以你应该自己保存"线程内部的线程"并简单地这样做:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,更好的是,只需将代码从MyTask.call()移动到doGet()方法即可.;)

旁白 - 关于你提到的10个同步servlet线程的限制:

这是一个(临时)设计决策,允许Google更轻松地控制其服务器上的负载(特别是servlet的内存使用).

您可以在此处找到有关这些问题的更多讨论:

这个话题一直困扰着我,因为我坚信超精简servlet代码,所以我常用的servlet可以轻松处理数百个(如果不是数千个)并发请求.由于每个实例的10个线程的任意限制而必须支付更多实例,至少可以说有点烦人.但阅读上面发布的链接,听起来他们已经意识到这一点,并正在努力寻找更好的解决方案.那么,让我们看看Google I/O 2013将在5月带来哪些公告... :)