Асинхронный сервлет
Иногда перед соответствующим сообщением сервлет будет выполнять некоторые трудоемкие операции, такие как операция JDBC или ожидание ответа от другого удаленного веб-сайта. Ожидание блокировки в синхронном сервлете приведет к снижению общей вычислительной способности веб-контейнера. . В этом случае может быть использован метод асинхронной обработки сервлета, а трудоемкая операция может быть помещена в другой поток для обработки, при этом связанные объекты запроса и ответа могут быть сохранены, а результат обработки может быть уведомлен клиент после завершения обработки.
Сервлет синхронизации
Как показано на рисунке, клиентский запрос Tomcat обрабатывается конвейером и, наконец, проходит через конвейер контейнера-оболочки. В это время он вызывает метод службы экземпляра сервлета для логической обработки. После обработки он ответит клиенту Весь процесс обрабатывается пулом потоков Tomcat Executor.
![img](file:///var/folders/74/8gxv5r0j3xvflks6fxth63540000gn/T/WizNote/de95c2ac-493a-4c0b-ac87-f45447bb6345/index_files/73010586.png)
Недостатки: задачи, которые требуют много времени для обработки логики, будут занимать поток обработки tomcat в течение длительного времени, что влияет на общую вычислительную мощность tomcat. Особенно, когда у вас есть ограниченное количество потоков, например только 5, и вы сталкиваетесь с 5 запросами, которые одновременно занимают 100 мс, другие запросы в течение этих 100 мс не будут выполнены, что серьезно повлияет на пропускную способность и стабильность службы.
Преимущества: Реализация логики проста для понимания, что хорошо для большого количества коротких задач, отнимающих много времени.
Асинхронный сервлет
Асинхронный сервлет был представлен в версии 3.0. Когда поступает запрос клиента, он наконец попадает в конвейер контейнера-оболочки через конвейер.После вызова службы экземпляра сервлета создается асинхронный контекст для инкапсуляции трудоемких логических операций и пусть пользователь определяет его самостоятельно.В это время поток обработки Tomcat может немедленно вернуться в пул потоков Executor, не дожидаясь завершения трудоемкой операции, прежде чем отказаться от потока, тем самым улучшая общие возможности обработки Tomcat.
![img](file:///var/folders/74/8gxv5r0j3xvflks6fxth63540000gn/T/WizNote/de95c2ac-493a-4c0b-ac87-f45447bb6345/index_files/73445233.png)
один 🌰
Инициализировать пул потоков
@WebListener
public class AppContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
// create the thread pool
ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));
servletContextEvent.getServletContext().setAttribute("executor",
executor);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent
.getServletContext().getAttribute("executor");
executor.shutdown();
}
}
Напишите прослушиватель для прослушивания некоторых действий в жизненном цикле асинхронной задачи.
public class AppAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onComplete");
// we can do resource cleanup activity here
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onError");
//we can return error response to client
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onStartAsync");
//we can log the event here
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
System.out.println("AppAsyncListener onTimeout");
//we can send appropriate response to client
ServletResponse response = asyncEvent.getAsyncContext().getResponse();
PrintWriter out = response.getWriter();
out.write("TimeOut Error in Processing");
}
}
Конкретная бизнес-логика помещается в класс реализации Runable.
public class AsyncRequestProcessor implements Runnable {
private AsyncContext asyncContext;
private int secs;
public AsyncRequestProcessor() {
}
public AsyncRequestProcessor(AsyncContext asyncCtx, int secs) {
this.asyncContext = asyncCtx;
this.secs = secs;
}
@Override
public void run() {
System.out.println("Async Supported? "
+ asyncContext.getRequest().isAsyncSupported());
longProcessing(secs);
try {
PrintWriter out = asyncContext.getResponse().getWriter();
out.write("Processing done for " + secs + " milliseconds!!");
} catch (IOException e) {
e.printStackTrace();
}
//complete the processing
asyncContext.complete();
}
private void longProcessing(int secs) {
// wait for given time before finishing
try {
Thread.sleep(secs);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Наконец, реализация асинхронного сервлета
@WebServlet(urlPatterns = "/AsyncLongRunningServlet", asyncSupported = true)
public class AsyncLongRunningServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
System.out.println("AsyncLongRunningServlet Start::Name="
+ Thread.currentThread().getName() + "::ID="
+ Thread.currentThread().getId());
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
String time = request.getParameter("time");
int secs = Integer.valueOf(time);
// max 10 seconds
if (secs > 10000) {
secs = 10000;
}
AsyncContext asyncCtx = request.startAsync();
asyncCtx.addListener(new AppAsyncListener());
asyncCtx.setTimeout(9000);
ThreadPoolExecutor executor = (ThreadPoolExecutor) request
.getServletContext().getAttribute("executor");
executor.execute(new AsyncRequestProcessor(asyncCtx));
long endTime = System.currentTimeMillis();
System.out.println("AsyncLongRunningServlet End::Name="
+ Thread.currentThread().getName() + "::ID="
+ Thread.currentThread().getId() + "::Time Taken="
+ (endTime - startTime) + " ms.");
}
}
PS:После использования аннотации @ServletComponentScan в SpringBootApplication Servlet, Filter и Listener могут быть автоматически зарегистрированы напрямую через аннотации @WebServlet, @WebFilter, @WebListener без дополнительного кода.