架构

Web 服务持有路径,Tomcat 持有 Service(默认实现为 StandardService),Service 持有 Connector 和 Container,业务上来看,一个 Service 对应一个域名。

嵌入式容器 demo 代码

下面展示了启动嵌入式 Tomcat 容器代码,基本与 Spring Boot 保持了一致。

Tomcat tomcat = new Tomcat();
File baseDir = new File("/Users/xuzeng/Documents/usr/Java/private/network-things/servlets/tomcat_file");
tomcat.setBaseDir(baseDir.getAbsolutePath());

Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");

tomcat.getService().addConnector(connector);
connector.setPort(8026);
connector.setAllowTrace(true);
connector.setURIEncoding(StandardCharsets.UTF_8.name());

tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());

// 注册 Conntext Servlet
prepareContext(tomcat.getHost(), new HelloServlet());

for (Container child : tomcat.getHost().findChildren()) {
    if(child instanceof StandardContext){
        StandardContext sc =  ((StandardContext) child);
        sc.loadOnStartup(sc.findChildren());
    }
}
tomcat.start();

在 Spring Boot 中,ServletContainerInitializer 被封装成了 TomcatStarter(对应 HTTP) 和 WsSci(对应 WebSocket),对 WebMVC 只暴露了 org.springframework.boot.web.servlet.ServletContextInitializer 接口,并提供了默认实现 ServletRegistrationBean,所以只要声明一个 ServletRegistrationBean bean 即可注册 Servlet。

private static void configureEngine(Engine engine) {
    engine.setBackgroundProcessorDelay(30);
    engine.getService().addLifecycleListener(System.out::println);
}

private static void prepareContext(Host host, ServletContainerInitializer... initializers) {
    StandardContext context = new StandardContext();

    context.setPath("");
    context.setDocBase("/Users/xuzeng/Documents/usr/Java/private/network-things/servlets/tomcat_file/docbase");
    context.setName("");
    context.setDisplayName("");
    context.addLifecycleListener(new Tomcat.FixContextListener());

    // 往 Context 添加 ServletContainerInitializer,可以利用此回调接口添加 Servlet
    for (ServletContainerInitializer sci : initializers){
        context.addServletContainerInitializer(sci, Collections.emptySet());
    }

    context.addValve(new ValveBase() {
        @Override
        public void invoke(Request request, Response response) throws IOException, ServletException {
            System.out.println(request);
            System.out.println(response);
            getNext().invoke(request, response);
        }
    });
    host.addChild(context);
}

Hello Servlet

public class HelloServlet extends HttpServlet implements ServletContainerInitializer {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("hello servlet service");
        super.service(req, res);
    }

    // 该方法将在 Tomcat 初始化期间被调用,注册 Servlet
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) {
        ServletRegistration.Dynamic rd = ctx.addServlet("hello-servlet", this);
        rd.addMapping("/");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletOutputStream out = resp.getOutputStream();
        out.write("hello heroku\\n".getBytes());
        out.flush();
        out.close();
    }
}

基本处理流程

网络处理流程

//todo 逻辑整理

http1.1