Как бороться с потенциально длительной операцией в doGet или doPost? - PullRequest
2 голосов
/ 22 декабря 2010

После HTTP POST (генерируется приложением, а не пользователями) я хочу отправить электронное письмо.Я правильно понял процедуру отправки электронной почты, но я не уверен в том, как работают серверы веб-приложений Java.

Меня особенно беспокоит тайм-ауты, и я хочу знать, не блокирую ли я какой-либо важный поток.

Если я сделаю что-то вроде следующего:

    @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 (тайм-аут, который я установилдо нескольких секунд).

Какой поток я тогда блокирую?Я имею в виду, очевидно, я понимаю, что я блокирую поток, который обрабатывает этот POST, но это проблема?Что этот поток должен делать дальше?

Я прочитал, что не должен создавать новые потоки на сервере веб-приложений Java, поэтому я полагаю, что не должен делать следующее правильно?

    Thread t = new Thread( new Runnable() {
        public void run() {
            sendEmail();
        }
    });
    t.start();

Обратите внимание, что мой вопрос не относится к отправке электронной почты: я хочу понять, о чем нужно заботиться в веб-приложении Java каждый раз, когда вы планируете выполнить потенциально блокирующую / долгую операцию после GET или POST.

Ответы [ 2 ]

2 голосов
/ 22 декабря 2010

Можно запустить поток внутри сервлета, просто нужно быть очень осторожным, чтобы убедиться, что он умирает, когда сервлет не развернут.

Самый простой способ сделать это - создать java.util.concurrent.ExecutorService при запуске сервлета и закрыть его при уничтожении сервлета. Затем вы можете отправить свои задания электронной почты в службу исполнителя и вернуться с номера doPost. Обратите внимание, что некоторые электронные письма могут не отправляться, если сервлет уничтожен после того, как задание поставлено в очередь.

В коде:

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();}});
    }
}
1 голос
/ 22 декабря 2010

Мой совет: сохраняйте задачи, которые вам нужно выполнить (т.е. в базе данных или очереди), чтобы обработать их в фоновом режиме с помощью второго процесса, и ваш doPost / doGet вернется как можно скорее. Пользователи не хотят ждать.

Например, вы можете получать запросы внешних приложений, сохранять электронную почту, которую нужно отправить, в базу данных или помещать ее в очередь JMS (многие серверы приложений имеют функции JMS, но я никогда не использовал их) и возвращать , Другой процесс может считывать эту базу данных / очередь и отправлять электронные письма, не блокируя ответы HTTP.

Об использовании потоков в вашем веб-приложении это будет работать, и это, вероятно, самое простое решение, но оно также может иметь проблемы с масштабируемостью. Если вы идете этим путем, убедитесь, что вы используете какой-то пул потоков (ExecutorService ...), потому что веб-серверы / операционные системы обычно имеют ограничение на количество потоков.

...