引言

基于 Spring Boot 的 web 应用中,在 spring-boot 包内包含了内置的 web server,具体包括 tomcatjettyundertownetty

本文旨在厘清 spring boot 中内置 server 的原理和使用。

Spring Boot Server 源码剖析

首先我们跟踪一下 org.springframework.boot:spring-boot 包中 org.springframework.boot.web package 下的代码,代码可以看 Github spring-boot

package 下的目录结构如下:

  • embedded:主要是存放内置的 web server,包含 WebServer 和 WebServerFactory 等实现,同时用到对应 server 的 package

  • reactive:基于 reactive 的 spring boot 应用,依赖 spring-boot-starter-webflux

  • servlet:基于 servlet 的 spring boot 应用,依赖 spring-boot-starter-web

  • server:servlet 和 reactive 中通用的 server 相关的类和接口

Servlet 和 Reactive

Spring Framework 5.0 提供了两种 web 框架,分别是 servletreactive ,他们具体的不同如下

  • servlet 是 Spring Web 中引入,基于 servlet API 实现,即 Spring MVC;

    reactive 是通过 Spring Webflux 引入,实现了 Reactive Streams 规范的响应式

  • servlet 实现的是阻塞 IO (blocking I/O)

    reactive 实现的是 Event-Loop,非阻塞 IO(non-blocking I/O),即异步处理

  • 针对 servlet 框架,SpringBoot 提供了三种内置 Server,分别是 JettyTomcatUndertow

    针对 reactive 框架,SpringBoot 提供了四种内置 Server,分别是 JettyTomcatUndertowNetty

WebServer

这部分我们介绍一下 WebServer 接口与实现,WebServer 提供的 5 个方法如下:

  • start:启动服务

  • stop:停止服务

  • getPort:提供端口号

  • shutDownGracefully: 优雅地关闭服务,在尝试结束服务的时候调用,处理一些被阻止的请求

  • destroy:销毁服务使其无法再次启动,默认调用 stop 方法

然后我们来看看 WebServer 的实现,实现主要是 spring-boot 中内置的几个 Server,具体有:

  • JettyWebServer:基于 Jetty 的 WebServer 实现,依赖于 org.eclipse.jetty

  • NettyWebServer:基于 Netty 的 WebServer 实现,依赖于 io.projectreactorio.netty

  • TomcatWebServer:基于 Tomcat 的 WebServer 实现,依赖于 org.apache.tomcat

  • UndertowWebServer:基于 Undertow 的 WebServer 实现,依赖于 io.undertow

目前我们知道了 SpringBoot 中内置的 WebServer 提供了这些实现,那么在服务启动时,默认使用的是哪个 Server 呢?

  • 启用 servlet 框架的情况下,默认使用的是 TomcatWebServer,因为spring-boot-starter-web 中默认依赖了 spring-boot-starter-tomcat

  • 启用 reactive 框架的情况下,默认使用的是 NettyWebServer,因为spring-boot-starter-webflux 中默认依赖了 spring-boot-starter-reactor-netty

WebServerFactory

SpringBoot 中提供了 WebServerFactory 的工厂接口类,用来封装 WebServer 的创建

WebServerFactory 也分为了两种即 ServletWebServerFactoryReactiveWebServerFactory,两者都提供了 getWebServer 方法来创建 WebServer,但具体有所不同

  • ServletWebServerFactory 提供的 getWebServer 方法传入的参数是 org.springframework.boot.web.servlet.ServletContextInitializer,用于配置 Servlet 3.0+ 的接口。

    ServletContextInitializer 中提供了 onStartup(ServletContext servletContext) 方法

    其中 jakarta.servlet.ServletContext 用于连接对应的 servlet 容器

  • ReactiveWebServerFactory 提供的 getWebServer 方法传入的参数是 org.springframework.http.server.reactive.HttpHandler

    HttpHandler 中提供了 handle(ServerHttpRequest request, ServerHttpResponse response) 方法

    其中 org.springframework.http.server.reactive.ServerHttpRequestorg.springframework.http.server.reactive.ServerHttpResponse 对应的则是基于 reactive server 的 request 和 response

Server

Servlet stack

Reactive stack

Tomcat

TomcatServletWebServerFactory

TomcatReactiveWebServerFactory

Jetty

JettyServletWebServerFactory

JettyReactiveWebServerFactory

Undertow

UndertowServletWebServerFactory

UndertowReactiveWebServerFactory

Reactor

N/A

NettyReactiveWebServerFactory

WebServerFactory 具体的接口和实现类如下图,这里不再做详细说明

Server 的 Bean 管理

上面我们梳理了 WebServer 和 用于管理的工厂类 WebServerFactory,下面我们来剖析 SpringBoot 中是如何通过 Factory 管理 Server Bean 的。

创建了对应的 ServletWebServerApplicationContextReactiveWebServerApplicationContext ,并在这两个类实现了 onRefresh() 方法,此方法用于在特定 context 子类中初始化特殊的 bean,这里所指的特定 context 子类包含 ServletWebServerApplicationContextReactiveWebServerApplicationContextStaticWebApplicationContext,具体如下:

  • ServletWebServerApplicationContext 中,onRefresh() 方法中获取 ServletWebServerFactory Bean 创建 WebServer

  • ReactiveWebServerApplicationContext 中,onRefresh() 方法中获取 ReactiveWebServerFactory Bean,并创建 WebServerManager,在 WebServerManager 的构造方法中创建了 WebServer

  • StaticWebApplicationContext 中,onRefresh() 方法用于创建 ThemeSource 的静态主题

内置 Server 简述:Jetty/Netty/Tomcat/Undertow

这里不会展开介绍这 4 种 Server 的实现和区别,只是稍微简述一下实现和不同点。

Tomcat

Tomcat 目前收录与 Apache 项目中,官方链接是https://tomcat.apache.org/

Tomcat 是主流的 Java Web Server,所以是十分稳定和成熟的,同时社区活跃文档和资源丰富。

Tomcat 可以支持 Http, Http/2 , AJP, WebSocket 协议,支持 Servlet 6.0

Jetty

Jetty 是 Eclipse 提供的一款 Server,官方链接是https://eclipse.dev/jetty/documentation.php

相比 Tomcat 它更加轻量级,有自己的异步支持。

Jetty 可以支持 Http, Http/2 ,Http/3, AJP, WebSocket 协议,支持 Servlet 6.0

Netty

Netty 是一个基于时间驱动的异步网络框架,被广泛应用于高性能的网络应用程序,尤其是处理大量并发连接的服务端应用,官方链接是https://netty.io/index.html

Netty 几乎支持了大部分的协议,有 **SSL/TLS, ** HTTP, ** HTTP/2**, HTTP/3, WebSockets, DNS, ** SPDY **, SMTP

Undertow

Undertow 是 JBoos 提供的一款 Server,官方地址是https://undertow.io/

Undertow 的特点在于轻量级、高性能和地资源消耗,同时支持嵌入式应用程序和微服务。

Undertow 可以支持 Http, Http/2 , WebSocket 协议,支持 Servlet 4.0

Server 对比

这里贴一下基于 Tomcat/Jetty/Undertow 的 Server 运行情况,主要监控了 内存使用、类加载、线程数、每秒请求书 和 每个请求的平均处理时长。

我们可以得知,内存占用和类加载方面,Jetty 更加轻量;处理请求数和响应时间方面,Undertow 速度更快。

Metric

Tomcat

Jetty

Undertow

jvm.memory.used (MB)

168

155

9787

jvm.classes.loaded

9869

9784

9787

jvm.threads.live

25

17

19

Requests per second

1542

1627

1650

Average time per request (ms)

6.483

6.148

6.059

如何在 Spring-Boot 中配置其他 Server

最后一部分我们介绍一下如何在 SpringBoot 中使用非默认的 Server

使用 Jetty Server

在 Servlet 框架下使用 Jetty:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

在 Reactive 框架下使用 Jetty:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-reactor-netty</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

使用 Undertow Server

在 Servlet 框架下使用 Undertow:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

在 Reactive 框架下使用 Undertow:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-reactor-netty</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

在 Reactive 框架下使用 Tomcat

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-reactor-netty</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>

在 Reactive 框架下支持 Netty, 且兼容 Servlet

如果原有是基于 SpringMVC 的项目,想使用 Netty Server,需要依赖 SpringWebflux

值得注意的是 SpringWebflux 也是支持 @Controller、@RestController 等传统 SpringMVC 注解的,具体实现的类是 AnnotationConfigReactiveWebServerApplicationContext ,它是 ReactiveWebServerApplicationContext 的子类。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
		<exclusion>
			<groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

参考

https://www.infoq.cn/article/servlet-and-reactive-stacks-spring-framework-5https://examples.javacodegeeks.com/java-development/enterprise-java/spring/tomcat-vs-jetty-vs-undertow-comparison-of-spring-boot-embedded-servlet-containers/https://medium.com/@the.raj.saxena/springboot-2-performance-servlet-stack-vs-webflux-reactive-stack-528ad5e9dadc