При завершении работы я ожидаю, что Tomcat перестанет принимать новые запросы (и это делает!) И успешно завершит текущие запросы.К сожалению, ServletContext (Servlet.destroy
, ServletContextListener.contextDestroyed
, ...) уничтожается до завершения текущих запросов.Эти текущие запросы, основанные на ServletContext, не будут выполнены, и потенциально поврежденные данные !Контекст должен быть уничтожен только после обработки текущих запросов.
Погружаясь в код, метод org.apache.catalina.core.StandardService.stopInternal
действительно показывает, что ServletContext уничтожается (оператор engine.stop()
) перед закрытием (оператор connector.stop()
) пула потоков длязапросы (ThreadPoolExecutor).
Я нашел параметр StandardContext.unloadDelay
(Количество мс, что контейнер будет ожидать выгрузки сервлетов. Если не указано, значение по умолчанию составляет 2000 мс.), что может быть решением...
Знаете ли вы, как по-другому решить эту проблему?
Как воспроизвести:
@WebServlet(name = "StartStopServlet", displayName = "StartStopServlet", urlPatterns = "/execute")
public class StartStopServlet extends javax.servlet.http.HttpServlet {
private ExpensiveResource resource = null;
@Override
public void init() throws ServletException {
System.out.println("----> initializing StartStopServlet ...");
super.init();
resource = new ExpensiveResource();
resource.connect();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// simulating time consuming operation before invoking ExpensiveService
System.out.println("----> preparing parameters for ExpensiveResource call. It will take about 6 secs");
// Now is the time to stop Tomcat: invoke shutdown.sh (or shutdown.bat)
try {
Thread.currentThread().sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// produces NullPointerException if ServletContext is destroyed
Model result = resource.doWork();
// Preparing response...
}
@Override
public void destroy() {
System.out.println("----> destroying StartStopServlet ...");
super.destroy();
resource.disconnect();
resource = null;
}
}
resource.doWork()
оператор вызывается после destroy()
метода.