Java 如何处理doGet或doPost中的潜在长时间操作?
在一个HTTP帖子(由应用程序生成,而不是由用户生成)之后,我想发送一封电子邮件。我正确地完成了电子邮件发送过程,但我不确定JavaWebApp服务器是如何工作的 我特别担心超时,我想知道我是否以某种方式阻止了一个重要的线程 如果我这样做:Java 如何处理doGet或doPost中的潜在长时间操作?,java,multithreading,web-applications,servlets,timeout,Java,Multithreading,Web Applications,Servlets,Timeout,在一个HTTP帖子(由应用程序生成,而不是由用户生成)之后,我想发送一封电子邮件。我正确地完成了电子邮件发送过程,但我不确定JavaWebApp服务器是如何工作的 我特别担心超时,我想知道我是否以某种方式阻止了一个重要的线程 如果我这样做: @Override public void doPost( final HttpServletRequest req, final HttpServletResponse resp )
@Override
public void doPost(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException, ServletException {
final PrintWriter pw = resp.getWriter();
pw.write( ... );
pw.flush();
pw.close();
// Here I'm sending an email, this can potentially
// block until the email send procedure times out
// (the timeout is set to 5 seconds)
sendEmail(...);
}
如果电子邮件服务器关闭,线程将阻塞,直到我的sendEmail超时(超时时间设置为几秒钟)
然后我阻塞了哪个线程?我的意思是,显然我意识到我正在阻止处理这篇文章的线程,但这是一个问题吗?这个线程下一步应该做什么
我读到我不应该从JavaWebApp服务器创建新线程,所以我认为我不应该这样做,对吗
Thread t = new Thread( new Runnable() {
public void run() {
sendEmail();
}
});
t.start();
请注意,我的问题并不特定于电子邮件发送:我想了解每次您计划在GET或POST之后执行潜在的阻止/长时间操作时,Java webapp中必须注意的事项。我的建议:存储您需要执行的任务(即在数据库或队列中)通过第二个进程在后台处理它们,并使您的
doPost
/doGet
尽快返回。用户不想等待
例如,您可以接收外部应用程序请求,将需要发送的电子邮件存储在数据库中,或将其放入JMS队列(许多应用程序服务器都有JMS功能,但我从未使用过),然后返回。另一个过程可能是读取数据库/队列并发送电子邮件而不阻止HTTP响应
关于在你的webapp中使用线程,它会起作用,可能是最简单的解决方案,但也可能存在可伸缩性问题。如果这样做,请确保使用某种类型的线程池(
ExecutorService
…),因为web服务器/操作系统通常对线程数量有限制。在servlet中启动线程是可以的,您只需非常小心地确保在servlet未部署时线程死亡
最简单的方法是在servlet启动时创建java.util.concurrent.ExecutorService
,并在servlet被销毁时关闭它。然后,您可以向executor服务提交电子邮件作业,并从doPost
返回。请注意,如果在作业排队后servlet被破坏,则可能无法发送某些电子邮件
代码:
class EmailServlet extends HttpServlet {
private ExecutorService emailSender;
public void init() {
emailSender = Executors.newFixedThreadPool(1);
}
public void destroy() {
emailSender.shutdownNow();
}
public void doPost(...) {
...
emailSender.execute(new Runnable() {public void run() {sendEmail();}});
}
}
例如,我可以将来自所有帖子的电子邮件请求放在一个队列中,并让另一个(唯一的)线程将其出列并发送。但这意味着我必须至少产生一个其他线程。这样做可以吗?在我的例子中,执行POST的不是用户而是另一个应用程序。但是,不管怎样,这些帖子是由用户发送的,用户不是在我关闭流后就可以得到页面吗?(在尝试发送电子邮件之前我会这样做)是的,但即使应用程序得到响应,您的服务器仍可能面临可伸缩性问题。这可能取决于服务器配置,但是想象一下,如果您的服务器配置为支持500个同时请求,而您的插槽已经用完,因为所有这些请求都在发送电子邮件,那么会发生什么情况……明白了!这是一个非常简单的用例,所以我会尽量保持简单。我曾考虑只使用另一个应用程序来消费/发送电子邮件(电子邮件中的文本无论如何都存储在DB中),但我想知道是否可以通过直接从Webapp中使用它来保持它的“小”度(从而使我不必构建/部署/维护两个应用程序)。我将确保只使用一个线程发送所有这些电子邮件,并在.war取消部署时正确停止该线程。非常感谢。太好了,尽量保持简单。什么时候取消部署servlet?例如,当我取消部署并重新部署.war时?如果我不确定它是否会死掉,那么每次我在不重新启动Tomcat的情况下取消部署/重新部署我的.war时,Tomcat将有越来越多的线程不再执行任何操作?(我理解正确吗?;)这取决于容器,但是取消部署和重新部署。war应该调用
destroy
。您可能可以依靠容器正确地完成工作(但登录以确保这一点不会有什么坏处)。如果在一个servlet实例上有两个调用init
,我会非常惊讶,因为这不符合servlet规范。