返回介绍

9.3 嵌入式Web服务器

发布于 2021-03-17 13:07:53 字数 13732 浏览 903 评论 0 收藏 0

每个Spring Boot Web应用程序都包含一个嵌入式Web服务器。 此功能会导致许多操作方法问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。 本节回答了这些问题。

9.3.1 使用其他Web服务器

许多Spring Boot启动器都包含默认的嵌入式容器。

  • 对于servlet堆栈应用程序,spring-boot-starter-web包括tomcat,包括spring-boot-starter-tomcat,但你可以使用spring-boot-starter-jetty或spring-boot-starter-undertow。
  • 对于反应堆栈应用,spring-boot-starter-webflux包括反应器网络,包括spring-boot-starter-reactor-netty,但你可以使用spring-boot-starter-tomcat,spring-boot-starter-jetty或spring -boot-starter-undertow。

切换到其他HTTP服务器时,除了包含所需的依赖项外,还需要排除默认依赖项。 Spring Boot为HTTP服务器提供单独的启动程序,以帮助使此过程尽可能简单。

以下Maven示例显示如何排除Tomcat并为Spring MVC包含Jetty:

<properties>
	<servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
	<exclusions>
		<!-- Exclude the Tomcat dependency -->
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Servlet API的版本已被覆盖,因为与Tomcat 9和Undertow 2.0不同,Jetty 9.4不支持Servlet 4.0。

以下Gradle示例显示如何排除Netty并包含Spring WebFlux的Undertow:

configurations {
	// exclude Reactor Netty
	compile.exclude module: 'spring-boot-starter-reactor-netty'
}

dependencies {
	compile 'org.springframework.boot:spring-boot-starter-webflux'
	// Use Undertow instead
	compile 'org.springframework.boot:spring-boot-starter-undertow'
	// ...
}

spring-boot-starter-reactor-netty需要使用WebClient类,因此即使需要包含不同的HTTP服务器,也可能需要依赖Netty。

9.3.2 禁用Web服务器

如果您的类路径包含启动Web服务器所需的位,Spring Boot将自动启动它。 要禁用此行为,请在application.properties中配置WebApplicationType,如以下示例所示:

spring.main.web-application-type=none

9.3.3 更改HTTP端口

在独立应用程序中,主HTTP端口默认为8080,但可以使用server.port设置(例如,在application.properties中或作为System属性)。 由于轻松绑定了Environment值,您还可以使用SERVER_PORT(例如,作为OS环境变量)。

要完全关闭HTTP端点但仍创建WebApplicationContext,请使用server.port = -1。 (这样做有时对测试很有用。)

有关更多详细信息,请参阅“Spring Boot功能”部分中的“第4.6.4.4节”,“自定义嵌入式Servlet容器”或ServerProperties源代码。

9.3.4 使用随机未分配的HTTP端口

要扫描空闲端口(使用OS本机来防止冲突),请使用server.port = 0。

9.3.5 在运行时发现HTTP端口

您可以从日志输出访问服务器运行的端口,也可以通过其WebServer访问ServletWebServerApplicationContext。 获得它并确保它已被初始化的最好方法是添加一个类型为ApplicationListener 的@Bean,并在发布时将容器拉出事件。

使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)的测试也可以使用@LocalServerPort批注将实际端口注入字段,如以下示例所示:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {

	@Autowired
	ServletWebServerApplicationContext server;

	@LocalServerPort
	int port;

	// ...

}

@LocalServerPort是@Value(“$ {local.server.port}”)的元注释。 不要尝试在常规应用程序中注入端口。 正如我们刚刚看到的那样,只有在容器初始化之后才设置该值。 与测试相反,应用程序代码回调会尽早处理(在值实际可用之前)。

9.3.6 启用HTTP响应压缩

Jetty,Tomcat和Undertow支持HTTP响应压缩。 它可以在application.properties中启用,如下所示:

server.compression.enabled=true

默认情况下,只有在内容类型为以下内容之一时才会压缩响应:

  • text/html
  • text/xml
  • text/plain
  • text/css
  • text/javascript
  • application/javascript
  • application/json
  • application/xml

您可以通过设置server.compression.mime-types属性来配置此行为。

9.3.7 配置SSL

可以通过设置各种server.ssl.*属性以声明方式配置SSL,通常在application.properties或application.yml中。 以下示例显示了在application.properties中设置SSL属性:

server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret

有关所有受支持属性的详细信息,请参阅Ssl。

使用上述示例之类的配置意味着应用程序不再支持端口8080上的普通HTTP连接器.Spring Boot不支持通过application.properties配置HTTP连接器和HTTPS连接器。 如果要同时使用两者,则需要以编程方式配置其中一个。 我们建议使用application.properties来配置HTTPS,因为HTTP连接器更容易以编程方式配置。 有关示例,请参阅spring-boot-sample-tomcat-multi-connectors示例项目。

9.3.8 配置HTTP/2

您可以使用server.http2.enabled配置属性在Spring Boot应用程序中启用HTTP/2支持。 此支持取决于所选的Web服务器和应用程序环境,因为JDK8不支持该协议。

Spring Boot不支持h2c,即HTTP/2协议的明文版本。 因此,您必须先配置SSL。

9.3.8.1 Undertow的HTTP/2

从Undertow 1.4.0+开始,支持HTTP/2,对JDK8没有任何额外要求。

9.3.8.2 Jetty的HTTP/2

从Jetty 9.4.8开始,Conscrypt库也支持HTTP / 2。 要启用该支持,您的应用程序需要有两个额外的依赖项:org.eclipse.jetty:jetty-alpn-conscrypt-server和org.eclipse.jetty.http2:http2-server。

9.3.8.3 Tomcat的HTTP/2

Spring Boot默认使用Tomcat 9.0.x,它在使用JDK 9或更高版本时支持HTTP/2的开箱即用。 或者,如果在主机操作系统上安装了libtcnative库及其依赖项,则可以在JDK 8上使用HTTP/2。

必须使库文件夹(如果尚未可用)到JVM库路径。 您可以使用JVM参数执行此操作,例如-Djava.library.path = /usr/local/opt/tomcat-native/lib。 有关更多信息请查看Tomcat官方文档。

在没有该本机支持的情况下在JDK 8上启动Tomcat 9.0.x会记录以下错误:

ERROR 8787 --- [           main] o.a.coyote.http11.Http11NioProtocol      : The upgrade handler [org.apache.coyote.http2.Http2Protocol] for [h2] only supports upgrade via ALPN but has been configured for the ["https-jsse-nio-8443"] connector that does not support ALPN.

此错误不是致命错误,应用程序仍以HTTP/1.1 SSL支持启动。

9.3.8.4 Reactor Netty的HTTP/2

spring-boot-webflux-starter默认使用Reactor Netty作为服务器。 可以使用JDK 9或更高版本的JDK支持为Reactor Netty配置HTTP/2。 对于JDK 8环境或最佳运行时性能,此服务器还支持具有本机库的HTTP/2。 要启用它,您的应用程序需要具有其他依赖项。

Spring Boot管理io.netty的版本:netty-tcnative-boringssl-static“uber jar”,包含适用于所有平台的本机库。 开发人员可以选择使用classifier仅导入所需的依赖项(请参阅Netty官方文档)。

9.3.9 配置Web服务器

通常,您应首先考虑使用众多可用配置键中的一个,并通过在application.properties(或application.yml或环境等)中添加新条目来自定义Web服务器,请参阅“第9.2.8 发现外部属性的内置选项”)。 server.* namespace在这里非常有用,它包括server.tomcat.*,server.jett.*等名称空间,用于特定于服务器的功能。 请参阅附录A,常见应用程序属性列表。

前面的部分介绍了许多常见用例,例如压缩,SSL或HTTP / 2。 但是,如果您的用例不存在配置键,则应该查看WebServerFactoryCustomizer。 您可以声明这样的组件并获得与您选择的服务器工厂相关的访问权限:您应该为所选服务器(Tomcat,Jetty,Reactor Netty,Undertow)和所选Web堆栈(Servlet或Reactive)选择变量。

以下示例适用于具有spring-boot-starter-web(Servlet堆栈)的Tomcat:

@Component
public class MyTomcatWebServerCustomizer
		implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

	@Override
	public void customize(TomcatServletWebServerFactory factory) {
		// customize the factory here
	}
}

另外Spring Boot提供:

ServerServlet stackReactive stack
TomcatTomcatServletWebServerFactoryTomcatReactiveWebServerFactory
JettyJettyServletWebServerFactoryJettyReactiveWebServerFactory
UndertowUndertowServletWebServerFactoryUndertowReactiveWebServerFactory
ReactorN/ANettyReactiveWebServerFactory

一旦您有权访问WebServerFactory,您通常可以向其添加定制器以配置特定部件,如连接器,服务器资源或服务器本身 - 所有这些都使用特定于服务器的API。

作为最后的手段,您还可以声明自己的WebServerFactory组件,它将覆盖Spring Boot提供的组件。 在这种情况下,您不能再依赖服务器命名空间中的配置属性。

9.3.10 向应用程序添加Servlet,Filter或Listener

在servlet堆栈应用程序中,即使用spring-boot-starter-web,有两种方法可以将Servlet API,Filter,ServletContextListener和Servlet API支持的其他侦听器添加到您的应用程序中:

  • 使用Spring Bean添加Servlet,Filter或Listener
  • 使用类路径扫描添加Servlet,Filter或Listener

9.3.10.1 使用Spring Bean添加Servlet,Filter或Listener

要使用Spring bean添加Servlet,Filter或Servlet * Listener,必须为其提供@Bean定义。 当您想要注入配置或依赖项时,这样做非常有用。 但是,您必须非常小心,它们不会导致太多其他bean的初始化,因为它们必须在应用程序生命周期的早期安装在容器中。 (例如,让它们依赖于您的DataSource或JPA配置并不是一个好主意。)您可以通过在首次使用而不是初始化时懒惰地初始化bean来解决此类限制。

对于Filters和Servlet,您还可以通过添加FilterRegistrationBean或ServletRegistrationBean来代替底层组件或添加底层组件来添加映射和init参数。

如果在过滤器注册中未指定dispatcherType,则使用REQUEST。 这与Servlet规范的默认调度程序类型一致。

像任何其他Spring bean一样,您可以定义Servlet过滤器bean的顺序; 请务必查看“注册Servlets, Filters, 和 Listeners 作为 Spring Beans“部分。

1)禁用Servlet或Filter的注册

如前所述,任何Servlet或Filter bean都会自动注册到servlet容器。 要禁用特定Filter或Servlet bean的注册,请为其创建注册Bean并将其标记为已禁用,如以下示例所示:

@Bean
public FilterRegistrationBean registration(MyFilter filter) {
	FilterRegistrationBean registration = new FilterRegistrationBean(filter);
	registration.setEnabled(false);
	return registration;
}

9.3.10.2 使用类路径扫描添加Servlet,Filter或Listener

通过使用@ServletComponentScan注释@Configuration类并指定包含要注册的组件的包,可以使用嵌入式servlet容器自动注册@WebServlet,@WebFilter和@WebListener注释类。 默认情况下,@ServletComponentScan会从带注释的类的包中进行扫描。

9.3.11 配置访问日志记录

可以通过各自的命名空间为Tomcat,Undertow和Jetty配置访问日志。

例如,以下设置使用自定义模式在Tomcat上记录访问权限。

server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)

日志的默认位置是相对于Tomcat基目录的日志目录。 默认情况下,logs目录是临时目录,因此您可能希望修复Tomcat的基目录或使用日志的绝对路径。 在前面的示例中,日志在my-tomcat/logs中相对于应用程序的工作目录可用。

可以以类似的方式配置Undertow的访问日志记录,如以下示例所示:

server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a "%r" %s (%D ms)

日志存储在相对于应用程序工作目录的日志目录中。 您可以通过设置server.undertow.accesslog.directory属性来自定义此位置。

最后,Jetty的访问日志记录也可以配置如下:

server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=/var/log/jetty-access.log

默认情况下,日志会重定向到System.err。 有关更多详细信息,请参阅Jetty文档。

9.3.12 在前端代理服务器后面运行

您的应用程序可能需要发送302重定向或使用绝对链接将内容呈现回自身。 在代理后面运行时,调用者需要指向代理的链接,而不是托管应用程序的计算机的物理地址。 通常,这种情况是通过与代理的合同来处理的,代理会添加标题以告诉后端如何构建自己的链接。

如果代理添加了传统的X-Forwarded-For和X-Forwarded-Proto标头(大多数代理服务器都这样做),那么绝对链接应该正确呈现,只要在application.properties中将server.use-forward-headers设置为true即可。

如果您的应用程序在Cloud Foundry或Heroku中运行,则server.use-forward-headers属性默认为true。 在所有其他实例中,它默认为false。

9.3.12.1 自定义Tomcat的代理配置

如果使用Tomcat,还可以配置用于携带“forwarded”信息的标头名称,如以下示例所示:

server.tomcat.remote-ip-header=x-your-remote-ip-header
server.tomcat.protocol-header=x-your-protocol-header

Tomcat还配置了一个默认的正则表达式,该表达式匹配要信任的内部代理。 默认情况下,10/8,192.168/16,169.254/16和127/8中的IP地址是可信的。 您可以通过向application.properties添加条目来自定义值的配置,如以下示例所示:

server.tomcat.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}

仅当使用properties文件进行配置时,才需要双反斜杠。 如果使用YAML,则单个反斜杠就足够了,并且与前面示例中显示的值相等的值为192.168.\d{1,3}.\d{1,3}。

您可以通过将internal-proxies设置为空来信任所有代理(但在生产中不这样做)。

您可以通过关闭自动关闭(执行此操作,设置server.use-forward-headers = false)并在TomcatServletWebServerFactory bean中添加新的阀门实例来完全控制Tomcat的RemoteIpValve的配置。

9.3.13 使用Tomcat启用多个连接

您可以将org.apache.catalina.connector.Connector添加到TomcatServletWebServerFactory,它可以允许多个连接器,包括HTTP和HTTPS连接器,如以下示例所示:

@Bean
public ServletWebServerFactory servletContainer() {
	TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
	tomcat.addAdditionalTomcatConnectors(createSslConnector());
	return tomcat;
}

private Connector createSslConnector() {
	Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
	Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
	try {
		File keystore = new ClassPathResource("keystore").getFile();
		File truststore = new ClassPathResource("keystore").getFile();
		connector.setScheme("https");
		connector.setSecure(true);
		connector.setPort(8443);
		protocol.setSSLEnabled(true);
		protocol.setKeystoreFile(keystore.getAbsolutePath());
		protocol.setKeystorePass("changeit");
		protocol.setTruststoreFile(truststore.getAbsolutePath());
		protocol.setTruststorePass("changeit");
		protocol.setKeyAlias("apitester");
		return connector;
	}
	catch (IOException ex) {
		throw new IllegalStateException("can't access keystore: [" + "keystore"
				+ "] or truststore: [" + "keystore" + "]", ex);
	}
}

9.3.14 使用Tomcat的LegacyCookieProcessor

默认情况下,Spring Boot使用的嵌入式Tomcat不支持Cookie格式的“Version 0”,因此您可能会看到以下错误:

java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value

如果可能的话,您应该考虑将代码更新为仅存储符合以后Cookie规范的值。 但是,如果您无法更改cookie的编写方式,则可以将Tomcat配置为使用LegacyCookieProcessor。 要切换到LegacyCookieProcessor,请使用添加TomcatContextCustomizer的WebServerFactoryCustomizer bean,如以下示例所示:

@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
	return (factory) -> factory.addContextCustomizers(
			(context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
}

9.3.15 使用Undertow启用多个Listener

将UndertowBuilderCustomizer添加到UndertowServletWebServerFactory并向Builder添加一个侦听器,如以下示例所示:

@Bean
public UndertowServletWebServerFactory servletWebServerFactory() {
	UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
	factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {

		@Override
		public void customize(Builder builder) {
			builder.addHttpListener(8080, "0.0.0.0");
		}

	});
	return factory;
}

9.3.16 使用@ServerEndpoint创建WebSocket端点

如果要在使用嵌入式容器的Spring Boot应用程序中使用@ServerEndpoint,则必须声明单个ServerEndpointExporter @Bean,如以下示例所示:

@Bean
public ServerEndpointExporter serverEndpointExporter() {
	return new ServerEndpointExporter();
}

前面示例中显示的bean使用基础WebSocket容器注册任何带有@ServerEndpoint注释的bean。 当部署到独立的servlet容器时,此角色由servlet容器初始化程序执行,而不需要ServerEndpointExporter bean。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文