實(shí)戰(zhàn)!16條SpringBoot Web服務(wù)配置指南及優(yōu)化技巧
環(huán)境:SpringBoot3.2.5
1. 切換其它Web Server
對(duì)于 servlet 棧應(yīng)用程序,spring-boot-starter-web 通過包含 spring-boot-starter-tomcat 將 Tomcat 包括在內(nèi),但也可以使用 spring-boot-starter-jetty 或 spring-boot-starter-undertow 代替。
對(duì)于反應(yīng)堆棧應(yīng)用程序,spring-boot-starter-webflux 通過加入 spring-boot-starter-reactor-netty 包含了 Reactor Netty,但你也可以使用 spring-boot-starter-tomcat、spring-boot-starter-jetty 或 spring-boot-starter-undertow 代替。
當(dāng)切換到不同的 HTTP 服務(wù)器時(shí),需要將默認(rèn)依賴項(xiàng)替換為你需要的依賴項(xiàng)。為了幫助完成這一過程,Spring Boot 為每個(gè)受支持的 HTTP 服務(wù)器都提供了一個(gè)單獨(dú)的啟動(dòng)器。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除 Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用 Jetty 替換-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
注:替換web server后在application.yml中的配置要進(jìn)行相應(yīng)的調(diào)整
2. 禁用Web Server
如果在類路徑包含啟動(dòng) Web 服務(wù)器所需的組件,Spring Boot 會(huì)自動(dòng)啟動(dòng)它。要禁用此行為,請(qǐng)?jiān)?application.yml 中配置 WebApplicationType,如下示例:
spring:
main:
web-application-type: "none"
這樣配置后,在實(shí)例化ApplicationContext容器對(duì)象時(shí)將不會(huì)實(shí)例化WebServer相關(guān)的容器對(duì)象。
3. 修改端口
默認(rèn)為 8080,但可以使用 server.port 進(jìn)行設(shè)置(例如,在 application.properties 中或作為系統(tǒng)屬性)。由于放寬了環(huán)境值的綁定,你還可以使用 SERVER_PORT(例如,作為操作系統(tǒng)環(huán)境變量)。
若要完全關(guān)閉 HTTP 端點(diǎn),但仍要?jiǎng)?chuàng)建 WebApplicationContext,請(qǐng)使用 server.port=-1(這樣做有助于測(cè)試)。
4. 隨機(jī)分配端口
要掃描空閑端口(使用操作系統(tǒng)本地端口以防止沖突),請(qǐng)使用 server.port=0。
你還可以指定范圍內(nèi)隨機(jī)生成端口
server:
port: ${random.int[5000,10000]}
這里指定端口在5000~10000范圍內(nèi)。
5. 運(yùn)行時(shí)獲取 HTTP 端口
要想在運(yùn)行時(shí)獲取端口號(hào),你可以添加一下@Bean獲取
@Component
public class PackWebServerListener implements ApplicationListener<WebServerInitializedEvent> {
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
System.out.printf("服務(wù)運(yùn)行端口: %d%n", event.getWebServer().getPort()) ;
}
}
使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) 的測(cè)試也可以通過 @LocalServerPort 注解將實(shí)際端口注入字段,如下示例:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {
@LocalServerPort
int port ;
}
@LocalServerPort 是 @Value("${local.server.port}")的元注解。 不要嘗試在常規(guī)應(yīng)用程序中注入端口。只能在測(cè)試環(huán)境下使用
6. 啟用HTTP響應(yīng)壓縮
Jetty、Tomcat、Reactor Netty 和 Undertow 支持 HTTP 響應(yīng)壓縮。可在 application.properties 中啟用,如下所示:
server:
compression:
enabled: true
默認(rèn)情況下,響應(yīng)長度必須至少達(dá)到 2048 字節(jié)才能執(zhí)行壓縮??梢酝ㄟ^配置進(jìn)行修改大小,如下所示:
server:
compression:
min-response-size: 1024
默認(rèn)情況下,只有當(dāng)響應(yīng)的內(nèi)容類型為以下類型之一時(shí),才會(huì)對(duì)其進(jìn)行壓縮:
- text/html
- text/xml
- text/plain
- text/css
- text/javascript
- application/javascript
- application/json
- application/xml
可通過如下屬性進(jìn)行配置
server:
compression:
mime-types:
- xxx
這里是數(shù)組類型。
7. 配置SSL
SSL 可通過設(shè)置各種 server.ssl.* 屬性進(jìn)行聲明式配置,通常在 application.properties 或 application.yaml 中設(shè)置。下面的示例展示了使用 Java KeyStore 文件設(shè)置 SSL 屬性:
server:
port: 8443
ssl:
key-store: "classpath:pack.keystore"
key-store-password: "xxxooo"
key-password: "xxxooo"
密鑰庫可通過如下方式生成
keytool -genkey -alias pack -keyalg RSA -keystore f:/pack.keystore
使用上例這樣的配置意味著應(yīng)用程序不再支持 8080 端口的純 HTTP 連接器。Spring Boot 不支持通過 application.properties 同時(shí)配置 HTTP 連接器和 HTTPS 連接器。
8. 配置HTTP/2
可以使用 server.http2.enabled 配置屬性在 Spring Boot 應(yīng)用程序中啟用 HTTP/2 支持。h2(HTTP/2 over TLS)和 h2c(HTTP/2 over TCP)均受支持。要使用 h2,還必須啟用 SSL。未啟用 SSL 時(shí),將使用 h2c。例如,當(dāng)應(yīng)用程序在執(zhí)行 TLS 終止的代理服務(wù)器后面運(yùn)行時(shí),就可能需要使用 h2c。
使用 Tomcat 的 HTTP/2
Spring Boot 默認(rèn)隨附 Tomcat 10.1.x,它支持開箱即用的 h2c 和 h2。另外,如果主機(jī)操作系統(tǒng)安裝了 libtcnative 庫及其依賴項(xiàng),也可以使用 libtcnative 來支持 h2。
如果 JVM 庫路徑中還沒有庫目錄,則必須提供該庫目錄??梢允褂?JVM 參數(shù)(如 -Djava.library.path=/usr/local/opt/tomcat-native/lib)來這樣做。
9. 配置Web Server
一般來說,你應(yīng)該首先考慮使用許多可用配置的key(即在配置文件中進(jìn)行的配置)。server.* 命名空間在此非常有用,它包括 server.tomcat.*、server.jetty.* 等命名空間,用于實(shí)現(xiàn)特定于服務(wù)器的功能。當(dāng),相應(yīng)server.*下沒有你需要的配置,那么你可以通過如下編程方式進(jìn)行自定義配置
@Component
public class PackTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
// TODO
}
}
通過該種方式進(jìn)行針對(duì)性的配置。
10. 添加Servlet,F(xiàn)ilter或Listener
在基于 Servlet 棧應(yīng)用程序中,有兩種方法可以在應(yīng)用程序中添加 Servlet、Filter、ServletContextListener 和 Servlet API 支持的其他監(jiān)聽器:
- 定義Servlet, Filter, Listener類型的Bean對(duì)象
對(duì)于Filter和Servlet,你可以分別注冊(cè)FilterRegistrationBean 和 ServletRegistrationBean 來進(jìn)行更多的配置選項(xiàng)。
@Bean
public FilterRegistrationBean<CustomFilter> registration(CustomFilter filter) {
FilterRegistrationBean<CustomFilter> registration = new FilterRegistrationBean<>(filter) ;
registration.setUrlPatterns(Arrays.asList("/**")) ;
// ...
return registration;
}
- 通過掃描的機(jī)制自動(dòng)注冊(cè)
分別使用@WebServlet, @WebFilter, and @WebListener注解標(biāo)注對(duì)應(yīng)的類,然后在配置類上添加@ServletComponentScan注解。
11. 日志配置
訪問日志可通過 Tomcat、Undertow 和 Jetty 各自的命名空間進(jìn)行配置。
Tomcat
server:
tomcat:
basedir: "my-tomcat"
accesslog:
enabled: true
pattern: "%t %a %r %s (%D microseconds)"
Undertow
server:
undertow:
accesslog:
enabled: true
pattern: "%t %a %r %s (%D milliseconds)"
options:
server:
record-request-start-time: true
請(qǐng)注意,除了啟用訪問日志和配置其模式外,還啟用了記錄請(qǐng)求開始時(shí)間。在訪問日志模式中包含響應(yīng)時(shí)間 (%D) 時(shí)需要這樣做。日志存儲(chǔ)在相對(duì)于應(yīng)用程序工作目錄的日志目錄中??梢酝ㄟ^設(shè)置 server.undertow.accesslog.dir 屬性來自定義該位置。
Jetty
server:
jetty:
accesslog:
enabled: true
filename: "/var/log/jetty-access.log"
默認(rèn)情況下,日志會(huì)重定向到 System.err。
12. 自定義Tomcat代理配置
如果你使用Tomcat,還可以進(jìn)一步配置用于攜帶“轉(zhuǎn)發(fā)”信息的頭名稱,如下所示:
server:
tomcat:
remoteip:
remote-ip-header: "x-your-remote-ip-header"
protocol-header: "x-your-protocol-header"
Tomcat 還配置了一個(gè)正則表達(dá)式,用于匹配需要信任的內(nèi)部代理。有關(guān)其默認(rèn)值,請(qǐng)查看server.tomcat.remoteip.internal-proxies 。你可以通過在 application.properties 中添加一個(gè)條目來自定義閥門的配置,如下所示:
server:
tomcat:
remoteip:
internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}"
13. Tomcat注冊(cè)多個(gè)Connector
@Configuration(proxyBeanMethods = false)
public class MyTomcatConfiguration {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> connectorCustomizer() {
return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createConnector());
}
private Connector createConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setPort(8081) ;
return connector ;
}
}
這里又開啟了一個(gè)8081端口。
14. 開啟Tomcat MBean
嵌入式 Tomcat 的 MBean 注冊(cè)表默認(rèn)是禁用的。這最大限度地減少了 Tomcat 的內(nèi)存占用。你可以通過如下配置開啟
server:
tomcat:
mbeanregistry:
enabled: true
15. Undertow開啟多個(gè)Listener
向 UndertowServletWebServerFactory 添加一個(gè) UndertowBuilderCustomizer,并向 Builder 添加一個(gè)監(jiān)聽器,如下所示:
@Configuration(proxyBeanMethods = false)
public class MyUndertowConfiguration {
@Bean
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowListenerCustomizer() {
return (factory) -> factory.addBuilderCustomizers(this::addHttpListener);
}
private Builder addHttpListener(Builder builder) {
return builder.addHttpListener(8080, "0.0.0.0") ;
}
}
16. 使用@ServerEndpoint創(chuàng)建WebSocket端點(diǎn)
如果要在嵌入式容器的 Spring Boot 應(yīng)用程序中使用 @ServerEndpoint,則必須聲明一個(gè) ServerEndpointExporter @Bean,如下所示:
@Configuration(proxyBeanMethods = false)
public class MyWebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
上例中顯示的 Bean 會(huì)向底層 WebSocket 容器注冊(cè)任何帶有 @ServerEndpoint 注釋的 Bean。當(dāng)部署到獨(dú)立的 servlet 容器時(shí),這一角色由 servlet 容器初始化器執(zhí)行,而不需要 ServerEndpointExporter Bean。