Servlet 3.0的异步处理

发布时间:2024年01月02日

1、传统Servlet处理

Web容器会为每个请求分配一个线程,默认情况下,响应完成前,该线程占用的资源都不会被释放。若有些请求需要长时间(例如长处理时间运算、等待某个资源),就会长时间占用线程所需资源,若这类请求很多,许多线程资源都被长时间占用,会对系统的性能造成负担。?

2、新特性:异步处理

Servlet 3.0新增了异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成(例如长时间运算完成、所需资源已获得)时再对客户端进行响应。

Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:
第一步,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;
第二步,调用业务接口的某些方法,以完成业务处理;
第三步,根据处理的结果提交响应,Servlet 线程结束。
其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。

Servlet 3.0 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:
第一步,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;
第二步,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,
第三步,Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。
Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。
?

3、异步实现方案

请求入参ServletRequest对象新增了一个startAsync()方法。该方法用于开启异步,同时返回AsyncContext 异步上下文对象。

    public AsyncContext startAsync() throws IllegalStateException;

可以通过AsyncContext的getRequest()、getResponse()方法取得请求、响应对象,此次对客户端的响应将暂缓至调用AsyncContext的complete()dispatch()方法为止,前者表示响应完成,后者表示将调派指定的URL进行响应。

4、异步实现案例:

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringBootStudy</artifactId>
        <groupId>com.hsc.www</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SB_33_servlet_app</artifactId>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>net.dreamlu</groupId>
            <artifactId>mica-core</artifactId>
            <version>2.1.1-GA</version>
        </dependency>
    </dependencies>

</project>

?web.xml

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>
</web-app>
AsyncServlet.java:
package com.hsc.www.webFlux.servlet;

import net.dreamlu.mica.core.utils.$;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@WebServlet(name = "AsyncServlet", urlPatterns = {"/testAsyn"}, asyncSupported = true)
public class AsyncServlet extends GenericServlet {

    ExecutorService executorService = Executors.newFixedThreadPool(10);

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {
        AsyncContext asyncContext = servletRequest.startAsync();
        executorService.submit(new Task(asyncContext));

        PrintWriter out = asyncContext.getResponse().getWriter();
        out.println("<h1>"+ $.formatDateTime(new Date())+" service threadName:" + Thread.currentThread().getName() + "</h1>");
        out.flush();
    }

    public static class Task implements Runnable {
        private final AsyncContext asyncContext;

        public Task(AsyncContext asyncContext) {
            this.asyncContext = asyncContext;
        }

        @Override
        public void run() {
            try {
                HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();

                sleep(10);

                PrintWriter out = response.getWriter();
                out.println("<h1>"+ $.formatDateTime(new Date())+" task threadName:" + Thread.currentThread().getName() + "</h1>");
                out.flush();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                asyncContext.complete();
            }

        }
    }

    private static void sleep(int s) {
        try {
            Thread.sleep(s * 1000L);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Tomcat servlet3.0需要 tomcat 7或更高版本才支持

运行结果:

文章来源:https://blog.csdn.net/hong10086/article/details/135331325
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。