Асинхронный сервлет

задняя часть Tomcat

Асинхронный сервлет

Иногда перед соответствующим сообщением сервлет будет выполнять некоторые трудоемкие операции, такие как операция 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 без дополнительного кода.