How to deal with potentially long operation in a doGet or a doPost?
After an HTTP POST (generated by an application, not by users) I want to send an email. I got the email sending procedure right but I'm not sure about how Java webapp servers are working.
I'm particularly concerned by timeouts and I want to know if I'm somehow blocking an important thread.
If I do something like the following:
@Override
public void doPost(
final HttpServletRequest req,
final HttpServletResponse resp
) throws IOException, ServletException {
final Pr开发者_运维知识库intWriter 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(...);
}
And if the email server is down, the thread will block until my sendEmail times out (timeout which I set to a few seconds).
Which thread am I then blocking? I mean, obviously I realize I'm blocking the thread that is processing this POST but is this an issue? What is this thread supposed to do next?
I read that I shouldn't create new threads from a Java webapp server, so I take it I shouldn't do the following right?
Thread t = new Thread( new Runnable() {
public void run() {
sendEmail();
}
});
t.start();
Note that my question ain't specific to email sending: I want to understand what has to be taken care of in a Java webapp everytime you plan to do a potentially blocking/long operation after a GET or a POST.
It is OK to start a thread inside a servlet, you just need to be very careful to make sure it dies when the servlet is undeployed.
The easiest way to do this would be to create a java.util.concurrent.ExecutorService
when the servlet starts and shut it down when the servlet is destroyed. You can then submit your email jobs to the executor service and return from doPost
. Note that some emails may not get sent if the servlet is destroyed after a job is queued.
In code:
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();}});
}
}
My advice: store the tasks you need to do (i.e. in a database or queue) to process them in the background by a second process and make your doPost
/doGet
return as soon as possible. Users do not want to wait.
For example, you could receive the external app requests, store the email you need to send in a database or put it into a JMS queue (many application servers come with JMS features but I've never used them), and return. Other process could be reading that database/queue and sending emails without blocking HTTP responses.
About using Threads in your webapp, it will work and it is probably the simplest solution, but it could also have scalability issues. If you go that way, make sure you use some kind of thread pool (ExecutorService
...) because web servers/operating systems usually have a limit on the number of threads.
精彩评论