Java 从 Tomcat 中的 servlet 生成线程的推荐方法是什么
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3745905/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is recommended way for spawning threads from a servlet in Tomcat
提问by Ritesh M Nayak
Probably a repeat! I am using Tomcat as my server and want to know what is best way to spawn threads in the servlet with deterministic outcomes. I am running some long running updates from a servlet action and would like for the request to complete and the updates to happen in the background. Instead of adding a messaging middleware like RabbitMQ, I thought I could spawn a thread that could run in the background and finish in its own time. I read in other SO threads that the server terminates threads spawned by the server in order for it to manage resources well.
大概是重蹈覆辙!我使用 Tomcat 作为我的服务器,并想知道在 servlet 中生成具有确定性结果的线程的最佳方法是什么。我正在从 servlet 操作运行一些长时间运行的更新,并希望完成请求并在后台进行更新。与其添加像 RabbitMQ 这样的消息中间件,我想我可以生成一个可以在后台运行并在自己的时间完成的线程。我在其他 SO 线程中读到,服务器终止了服务器产生的线程,以便它能够很好地管理资源。
Is there a recommended way of spawning threads, background jobs when using Tomcat. I also use Spring MVC for the application.
使用 Tomcat 时是否有推荐的产生线程、后台作业的方法。我还为应用程序使用了 Spring MVC。
采纳答案by BalusC
In a barebones servletcontainer like Tomcat or Jetty, your safest bet is using an applicaton wide thread poolwith a max amount of threads, so that the tasks will be queued whenever necessary. The ExecutorService
is very helpful in this.
在像 Tomcat 或 Jetty 这样的准系统 servletcontainer 中,您最安全的选择是使用具有最大线程数的应用程序宽线程池,以便任务将在必要时排队。该ExecutorService
是在这个非常有帮助。
Upon application startup or servlet initialization use the Executors
class to create one:
在应用程序启动或 servlet 初始化时,使用Executors
该类创建一个:
executor = Executors.newFixedThreadPool(10); // Max 10 threads.
Then during servlet's service (you could ignore the result for the case that you aren't interested, or store it in the session for later access):
然后在 servlet 的服务期间(对于您不感兴趣的情况,您可以忽略结果,或者将其存储在会话中以供以后访问):
Future<ReturnType> result = executor.submit(new YourTask(yourData));
Where YourTask
must implement Runnable
or Callable
and can look something like this, whereby yourData
is just your data, e.g. populated with request parameter values (just keep in mind that you should absolutely not pass Servlet API artifacts such as HttpServletRequest
or HttpServletResponse
along!):
哪里YourTask
必须实现Runnable
或Callable
可以看起来像这样,其中yourData
只是您的数据,例如填充请求参数值(请记住,您绝对不应该传递 Servlet API 工件,例如HttpServletRequest
或HttpServletResponse
!):
public class YourTask implements Runnable {
private YourData yourData;
public YourTask(YourData yourData) {
this.yourData = yourData;
}
@Override
public void run() {
// Do your task here based on your data.
}
}
Finally, during application's shutdown or servlet's destroy you need to explicitly shutdown it, else the threads may run forever and prevent the server from properly shutting down.
最后,在应用程序关闭或 servlet 销毁期间,您需要显式关闭它,否则线程可能永远运行并阻止服务器正确关闭。
executor.shutdownNow(); // Returns list of undone tasks, for the case that.
In case you're actually using a normal JEE server such as WildFly, Payara, TomEE, etc, where EJB is normally available, then you can simply put @Asynchronous
annotation on an EJB method which you invoke from the servlet. You can optionally let it return a Future<T>
with AsyncResult<T>
as concrete value.
如果您实际上使用的是普通的 JEE 服务器,例如 WildFly、Payara、TomEE 等,其中 EJB 通常可用,那么您可以简单地将@Asynchronous
注释放在从 servlet 调用的 EJB 方法上。您可以选择让它返回一个Future<T>
withAsyncResult<T>
作为具体值。
@Asynchronous
public Future<ReturnType> submit() {
// ... Do your job here.
return new AsyncResult<ReturnType>(result);
}
see also:
也可以看看:
回答by dty
Strictly speaking, you're not allowed to spawn threads according to the Java EE spec. I would also consider the possibility of a denial of service attack (deliberate or otherwise) if multiple requests come in at once.
严格来说,您不能根据 Java EE 规范生成线程。如果同时有多个请求,我还会考虑拒绝服务攻击(有意或无意)的可能性。
A middleware solution would definitely be more robust and standards-compliant.
中间件解决方案肯定会更加健壮和符合标准。
回答by Jaydeep Patel
Spring supports asynchronous task (in your case long running) through spring-scheduling. Instead of using Java threads direct I suggest to use it with Quartz.
Spring 通过 spring-scheduling 支持异步任务(在您的情况下长时间运行)。我建议将它与 Quartz 一起使用,而不是直接使用 Java 线程。
Recourses:
资源:
回答by Pascal Thivent
You could maybe use a CommonJ WorkManager (JSR 237) implementation like Foo-CommonJ:
您可以使用像Foo-CommonJ这样的 CommonJ WorkManager (JSR 237) 实现:
CommonJ ? JSR 237 Timer & WorkManager
Foo-CommonJ is a JSR 237 Timer and WorkManager implementation. It is designed to be used in containers that do not come with their own implementation – mainly plain servlet containers like Tomcat. It can also be used in fully blown Java EE applications servers that do not have a WorkManager API or have a non-standard API like JBoss.
Why using WorkManagers?
The common use case is that a Servlet or JSP needs to aggregate data from multiple sources and display them in one page. Doing your own threading a managed environement like a J2EE container is inappropriate and should never be done in application level code. In this case the WorkManager API can be used to retrieve the data in parallel.
Install/Deploy CommonJ
The deployment of JNDI resources vendor dependant. This implementation comes with a Factory class that implements the
javax.naming.spi.ObjectFactory
interface with makes it easily deployable in the most popular containers. It is also available as a JBoss service. more...
通用J ? JSR 237 计时器和工作管理器
Foo-CommonJ 是一个 JSR 237 Timer 和 WorkManager 实现。它被设计用于没有自己实现的容器——主要是像 Tomcat 这样的普通 servlet 容器。它还可以用于没有 WorkManager API 或具有非标准 API(如 JBoss)的完全成熟的 Java EE 应用程序服务器。
为什么要使用 WorkManager?
常见的用例是 Servlet 或 JSP 需要聚合来自多个源的数据并将它们显示在一个页面中。对像 J2EE 容器这样的托管环境进行自己的线程处理是不合适的,永远不应该在应用程序级代码中完成。在这种情况下,WorkManager API 可用于并行检索数据。
安装/部署 CommonJ
JNDI 资源的部署依赖于供应商。此实现带有一个 Factory 类,该类实现了
javax.naming.spi.ObjectFactory
接口,使其可以轻松部署在最流行的容器中。它也可以作为 JBoss 服务使用。更多的...
Update:Just to clarify, here is what the Concurrency Utilities for Java EE Preview(looks like this is the successor of JSR-236 & JSR-237) writes about unmanaged threads:
更新:澄清一下,这里是Java EE 预览版的并发实用程序(看起来这是 JSR-236 和 JSR-237 的继承者)关于非托管线程的内容:
2.1 Container-Managed vs. Unmanaged Threads
Java EE application servers require resource management in order to centralize administration and protect application components from consuming unneeded resources. This can be achieved through the pooling of resources and managing a resource's lifecycle. Using Java SE concurrency utilities such as the
java.util.concurrency
API,java.lang.Thread
andjava.util.Timer
in a server application component such as a servlet or EJB are problematic since the container and server have no knowledge of these resources.By extending the
java.util.concurrent
API, application servers and Java EE containers can become aware of the resources that are used and provide the proper execution context for the asynchronous operations to run with.This is largely achieved by providing managed versions of the predominant
java.util.concurrent.ExecutorService
interfaces.
2.1 容器管理与非托管线程
Java EE 应用服务器需要资源管理,以便集中管理并保护应用程序组件免于消耗不需要的资源。这可以通过资源池和管理资源的生命周期来实现。使用 Java SE 并发实用程序(例如
java.util.concurrency
API)java.lang.Thread
以及java.util.Timer
在服务器应用程序组件(例如 servlet 或 EJB)中会出现问题,因为容器和服务器不知道这些资源。通过扩展
java.util.concurrent
API, 应用程序服务器和 Java EE 容器可以了解所使用的资源,并为异步操作提供正确的执行上下文以使用.这主要是通过提供主要
java.util.concurrent.ExecutorService
接口的托管版本来 实现的。
So nothing new IMO, the "old" problem is the same, unmanaged thread are still unmanaged threads:
所以没有什么新的 IMO,“旧”问题是一样的,非托管线程仍然是非托管线程:
- They are unknown to the application server and do not have access to Java EE contextual information.
- They can use resources on the back of the application server, and without any administration ability to control their number and resource usage, this can affect the application server's ability to recover resources from failure or to shutdown gracefully.
- 它们对于应用程序服务器来说是未知的,并且无权访问 Java EE 上下文信息。
- 它们可以使用应用服务器背面的资源,并且没有任何管理能力来控制它们的数量和资源使用情况,这会影响应用服务器从故障中恢复资源或正常关闭的能力。
References
参考
回答by Nereis
Since Spring 3, you can use @Async annotations:
从 Spring 3 开始,您可以使用 @Async 注释:
@Service
public class smg {
...
@Async
public getCounter() {...}
}
with <context:component-scan base-package="ch/test/mytest">
and <task:annotation-driven/>
in the context file
与<context:component-scan base-package="ch/test/mytest">
和<task:annotation-driven/>
上下文文件
Please refer to this tutorial: http://spring.io/blog/2010/01/05/task-scheduling-simplifications-in-spring-3-0/
请参考本教程:http: //spring.io/blog/2010/01/05/task-scheduling-simplifications-in-spring-3-0/
Works great for me on Tomcat7 and you don't have to managed a thread pool.
在 Tomcat7 上非常适合我,而且您不必管理线程池。
回答by cvnew
I know it is an old question, but people keep asking it, trying to do this kind of thing (explicitly spawning threads while processing a servlet request) all the time... It is a very flawed approach - for more than one reason... Simply stating that Java EE containers frown upon such practice is not enough, although generally true...
我知道这是一个老问题,但人们一直在问它,一直试图做这种事情(在处理 servlet 请求时显式生成线程)......这是一种非常有缺陷的方法 - 原因不止一个。 .. 仅仅说 Java EE 容器不赞成这种做法是不够的,尽管通常是正确的......
Most importantly, one can never predict how many concurrent requests the servlet will be receiving at any given time. A web application, a servlet, by definition, is meant to be capable of processing multiple requests on the given endpoint at a time. If you are programming you request processing logic to explicitly launch a certain number of concurrent threads, you are risking to face an all but inevitable situation of running out of available threads and choking your application. Your task executor is always configured to work with a thread pool that is limited to a finite reasonable size. Most often, it is not larger than 10-20 (you don't want too many threads executing your logic - depending on the nature of the task, resources they compete for, the number of processors on your server, etc.) Let's say, your request handler (e.g. MVC controller method) invokes one or more @Async-annotated methods (in which case Spring abstracts the task executor and makes things easy for you) or uses the task executor explicitly. As your code executes it starts grabbing the available threads from the pool. That's fine if you are always processing one request at a time with no immediate follow-up requests. (In that case, you are probably trying to use the wrong technology to solve your problem.) However, if it is a web application that is exposed to arbitrary (or even known) clients who may be hammering the endpoint with requests, you will quickly deplete the thread pool, and the requests will start piling up, waiting for threads to be available. For that reason alone, you should realize that you may be on a wrong path - if you are considering such design.
最重要的是,永远无法预测 servlet 在任何给定时间将接收多少并发请求。根据定义,Web 应用程序 servlet 旨在能够一次处理给定端点上的多个请求。如果您正在编程,您请求处理逻辑显式启动一定数量的并发线程,您将面临几乎不可避免的情况,即耗尽可用线程并阻塞您的应用程序。您的任务执行器始终配置为使用限制为有限合理大小的线程池。大多数情况下,它不大于 10-20(您不希望太多线程执行您的逻辑 - 取决于任务的性质、它们竞争的资源、服务器上的处理器数量等)让我们说,您的请求处理程序(例如 MVC 控制器方法)调用一个或多个 @Async 注释的方法(在这种情况下,Spring 抽象了任务执行程序并使事情变得容易)或显式使用任务执行程序。当您的代码执行时,它开始从池中获取可用线程。如果您总是一次处理一个请求而没有立即跟进请求,那很好。(在这种情况下,您可能正在尝试使用错误的技术来解决您的问题。)但是,如果它是一个暴露给任意(甚至已知)客户端的 Web 应用程序,这些客户端可能会用请求敲打端点,您将快速耗尽线程池,请求将开始堆积,等待线程可用。仅仅因为这个原因,你应该意识到你可能走错了路——如果你正在考虑这样的设计。当您的代码执行时,它开始从池中获取可用线程。如果您总是一次处理一个请求而没有立即跟进请求,那很好。(在这种情况下,您可能正在尝试使用错误的技术来解决您的问题。)但是,如果它是一个暴露给任意(甚至已知)客户端的 Web 应用程序,这些客户端可能会用请求敲打端点,您将快速耗尽线程池,请求将开始堆积,等待线程可用。仅仅因为这个原因,你应该意识到你可能走错了路——如果你正在考虑这样的设计。当您的代码执行时,它开始从池中获取可用线程。如果您总是一次处理一个请求而没有立即跟进请求,那很好。(在这种情况下,您可能正在尝试使用错误的技术来解决您的问题。)但是,如果它是一个暴露给任意(甚至已知)客户端的 Web 应用程序,这些客户端可能会用请求敲打端点,您将快速耗尽线程池,请求将开始堆积,等待线程可用。仅仅因为这个原因,你应该意识到你可能走错了路——如果你正在考虑这样的设计。) 但是,如果它是一个暴露给任意(甚至已知)客户端的 Web 应用程序,这些客户端可能会用请求敲打端点,您将很快耗尽线程池,并且请求将开始堆积,等待线程可用的。仅仅因为这个原因,你应该意识到你可能走错了路——如果你正在考虑这样的设计。) 但是,如果它是一个暴露给任意(甚至已知)客户端的 Web 应用程序,这些客户端可能会用请求敲打端点,您将很快耗尽线程池,并且请求将开始堆积,等待线程可用的。仅仅因为这个原因,你应该意识到你可能走错了路——如果你正在考虑这样的设计。
A better solution may be to stagethe data to be processed asynchronously (that could be a queue, or any other type of a temporary/staging data store) and return the response. Have an external, independent application or even multiple instances of it (deployed outside your web container) poll the staging endpoint(s) and process the data in the background, possibly using a finite number of concurrent threads. Not only such solution will give you the advantage of asynchronous/concurrent processing, but will also scale because you will be able to run as many instances of such poller as you need, and they can be distributed, pointing to the staging endpoint. HTH
一个更好的解决方案可以是阶段的数据被异步地处理(这可能是一个队列,或任何其它类型的临时/分段数据存储器),并返回该响应。让外部独立应用程序或什至它的多个实例(部署在 Web 容器外部)轮询暂存端点并在后台处理数据,可能使用有限数量的并发线程。这样的解决方案不仅可以为您提供异步/并发处理的优势,而且还可以扩展,因为您将能够根据需要运行尽可能多的此类轮询器实例,并且它们可以分布,指向登台端点。HTH