Appearance
Spring Boot 内嵌 Web 服务器完全指南 🚀
什么是内嵌 Web 服务器?
Spring Boot 最革命性的特性之一就是内嵌 Web 服务器。想象一下,在传统的 Java Web 开发中,你需要:
- 安装 Tomcat 服务器
- 配置服务器
- 打包成 WAR 文件
- 部署到服务器
而 Spring Boot 说:"不,这太复杂了!" 它直接把 Web 服务器打包到你的应用程序中,让你的应用变成一个可执行的 JAR 文件。
TIP
内嵌 Web 服务器的核心理念:应用即服务器。你的应用程序本身就包含了运行所需的一切,真正实现了"一键启动"。
支持的 Web 服务器类型
Spring Boot 支持多种内嵌 Web 服务器,每种都有其特色:
服务器 | 适用场景 | 特点 |
---|---|---|
Tomcat | 传统 Servlet 应用 | 默认选择,成熟稳定 |
Jetty | 高并发场景 | 轻量级,内存占用少 |
Undertow | 高性能需求 | 性能优异,支持 NIO |
Reactor Netty | 响应式应用 | 专为 WebFlux 设计 |
切换 Web 服务器
为什么要切换服务器?
不同的 Web 服务器有不同的优势:
- Tomcat:最成熟,文档丰富,生态完善
- Jetty:启动速度快,适合开发环境
- Undertow:性能最佳,内存效率高
- Reactor Netty:响应式编程的最佳选择
实际操作示例
kotlin
// build.gradle.kts
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web") {
// 排除默认的 Tomcat
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
}
// 使用 Jetty 替代
implementation("org.springframework.boot:spring-boot-starter-jetty")
}
kotlin
// build.gradle.kts
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web") {
exclude(group = "org.springframework.boot", module = "spring-boot-starter-tomcat")
}
implementation("org.springframework.boot:spring-boot-starter-undertow")
}
性能对比示例
kotlin
@RestController
class PerformanceTestController {
@GetMapping("/hello")
fun hello(): String {
return "Hello from ${getServerType()}!"
}
private fun getServerType(): String {
return when {
isClassPresent("org.apache.catalina.startup.Tomcat") -> "Tomcat"
isClassPresent("org.eclipse.jetty.server.Server") -> "Jetty"
isClassPresent("io.undertow.Undertow") -> "Undertow"
else -> "Unknown"
}
}
private fun isClassPresent(className: String): Boolean {
return try {
Class.forName(className)
true
} catch (e: ClassNotFoundException) {
false
}
}
}
端口配置与管理
基础端口配置
properties
# application.properties
server.port=9090 # 自定义端口
动态端口分配
在微服务架构中,经常需要动态分配端口:
properties
server.port=0 # 使用随机端口
运行时获取端口
kotlin
@Component
class PortDiscoveryService : ApplicationListener<WebServerInitializedEvent> {
private var actualPort: Int = 0
override fun onApplicationEvent(event: WebServerInitializedEvent) {
actualPort = event.webServer.port
println("服务器启动在端口: $actualPort")
}
fun getActualPort(): Int = actualPort
}
测试中的端口注入
kotlin
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WebIntegrationTest {
@LocalServerPort
private var port: Int = 0
@Test
fun `测试服务器端口`() {
println("测试服务器运行在端口: $port")
// 使用 port 进行测试
}
}
SSL/HTTPS 配置
为什么需要 HTTPS?
在现代 Web 开发中,HTTPS 已经成为标准:
- 保护数据传输安全
- 提升 SEO 排名
- 现代浏览器要求
- 符合安全规范
使用 Java KeyStore
yaml
# application.yml
server:
port: 8443
ssl:
key-store: "classpath:keystore.jks"
key-store-password: "secret"
key-password: "another-secret"
使用 PEM 证书文件
yaml
server:
port: 8443
ssl:
certificate: "classpath:my-cert.crt"
certificate-private-key: "classpath:my-cert.key"
trust-certificate: "classpath:ca-cert.crt"
SSL 配置服务
kotlin
@Configuration
class SSLConfiguration {
@Bean
fun sslCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { factory ->
factory.addConnectorCustomizers { connector ->
// 自定义 SSL 配置
connector.scheme = "https"
connector.secure = true
connector.port = 8443
}
}
}
}
WARNING
配置 HTTPS 后,应用将不再支持 HTTP 连接。如果需要同时支持 HTTP 和 HTTPS,需要通过编程方式配置额外的连接器。
HTTP/2 支持
HTTP/2 的优势
启用 HTTP/2
yaml
server:
http2:
enabled: true # 启用 HTTP/2
ssl:
enabled: true # HTTP/2 需要 SSL
HTTP/2 测试控制器
kotlin
@RestController
class Http2TestController {
@GetMapping("/api/data")
fun getData(): ResponseEntity<Map<String, Any>> {
val data = mapOf(
"message" to "HTTP/2 响应",
"timestamp" to System.currentTimeMillis(),
"protocol" to "HTTP/2.0"
)
return ResponseEntity.ok(data)
}
@GetMapping("/api/stream")
fun getStreamData(): Flux<String> {
return Flux.interval(Duration.ofSeconds(1))
.map { "数据流 #$it" }
.take(10)
}
}
响应压缩优化
压缩配置
yaml
server:
compression:
enabled: true
min-response-size: 1024 # 最小压缩大小
mime-types:
- text/html
- text/css
- application/json
- application/javascript
压缩效果测试
kotlin
@RestController
class CompressionTestController {
@GetMapping("/large-json")
fun getLargeJson(): Map<String, Any> {
// 生成大量数据用于测试压缩效果
val largeData = (1..1000).map { index ->
"item$index" to mapOf(
"id" to index,
"name" to "测试数据项 $index",
"description" to "这是一个用于测试压缩功能的长描述文本".repeat(5)
)
}.toMap()
return mapOf(
"data" to largeData,
"size" to largeData.size,
"compressed" to true
)
}
}
自定义 Web 服务器配置
高级配置场景
有时候,简单的属性配置无法满足复杂需求,这时就需要编程式配置:
kotlin
@Configuration
class CustomWebServerConfiguration {
@Bean
fun webServerCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { factory ->
// 自定义连接池配置
factory.addConnectorCustomizers { connector ->
val protocol = connector.protocolHandler as Http11NioProtocol
protocol.maxConnections = 200
protocol.maxThreads = 100
protocol.minSpareThreads = 10
protocol.connectionTimeout = 20000
}
// 添加自定义 Valve
factory.addContextCustomizers { context ->
val accessLogValve = AccessLogValve()
accessLogValve.directory = "logs"
accessLogValve.pattern = "%h %l %u %t \"%r\" %s %b %D"
context.parent.pipeline.addValve(accessLogValve)
}
}
}
}
多端口支持
kotlin
@Configuration
class MultiPortConfiguration {
@Bean
fun httpConnectorCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { factory ->
// 添加额外的 HTTP 连接器
val httpConnector = Connector("org.apache.coyote.http11.Http11NioProtocol")
httpConnector.port = 8080
httpConnector.scheme = "http"
httpConnector.secure = false
factory.addAdditionalTomcatConnectors(httpConnector)
}
}
}
代理服务器配置
反向代理场景
代理头处理配置
yaml
server:
forward-headers-strategy: NATIVE # 或 FRAMEWORK
tomcat:
remoteip:
remote-ip-header: x-forwarded-for
protocol-header: x-forwarded-proto
internal-proxies: 192\.168\.\d{1,3}\.\d{1,3}
代理信息获取
kotlin
@RestController
class ProxyInfoController {
@GetMapping("/proxy-info")
fun getProxyInfo(request: HttpServletRequest): Map<String, Any?> {
return mapOf(
"remoteAddr" to request.remoteAddr,
"forwardedFor" to request.getHeader("X-Forwarded-For"),
"forwardedProto" to request.getHeader("X-Forwarded-Proto"),
"forwardedHost" to request.getHeader("X-Forwarded-Host"),
"realIp" to getRealClientIp(request)
)
}
private fun getRealClientIp(request: HttpServletRequest): String {
val xForwardedFor = request.getHeader("X-Forwarded-For")
return if (!xForwardedFor.isNullOrBlank()) {
xForwardedFor.split(",").first().trim()
} else {
request.remoteAddr
}
}
}
访问日志配置
不同服务器的日志配置
yaml
server:
tomcat:
basedir: "logs"
accesslog:
enabled: true
pattern: "%t %a %r %s (%D microseconds)"
directory: "access-logs"
yaml
server:
undertow:
accesslog:
enabled: true
pattern: "%t %a %r %s (%D milliseconds)"
options:
server:
record-request-start-time: true
yaml
server:
jetty:
accesslog:
enabled: true
filename: "/var/log/jetty-access.log"
自定义访问日志处理
kotlin
@Component
class AccessLogProcessor {
private val logger = LoggerFactory.getLogger(AccessLogProcessor::class.java)
@EventListener
fun handleAccessLog(event: ServletRequestHandledEvent) {
val logEntry = buildString {
append("${event.requestUrl} ")
append("${event.method} ")
append("${event.statusCode} ")
append("${event.processingTimeMillis}ms")
}
logger.info("访问日志: $logEntry")
}
}
实际应用场景
微服务环境配置
kotlin
@Configuration
@Profile("microservice")
class MicroserviceWebConfiguration {
@Bean
fun microserviceCustomizer(): WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
return WebServerFactoryCustomizer { factory ->
// 微服务优化配置
factory.setPort(0) // 动态端口
// 健康检查端点
factory.addInitializers { servletContext ->
servletContext.addServlet("health", HealthCheckServlet())
.addMapping("/health")
}
}
}
}
class HealthCheckServlet : HttpServlet() {
override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
resp.contentType = "application/json"
resp.writer.write("""{"status":"UP","timestamp":"${System.currentTimeMillis()}"}""")
}
}
开发环境配置
kotlin
@Configuration
@Profile("dev")
class DevelopmentWebConfiguration {
@Bean
fun devCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { factory ->
// 开发环境快速重启配置
factory.addConnectorCustomizers { connector ->
connector.setProperty("connectionTimeout", "3000")
connector.setProperty("keepAliveTimeout", "3000")
}
}
}
}
性能监控与调优
服务器指标收集
kotlin
@Component
class WebServerMetrics {
private val meterRegistry: MeterRegistry by lazy {
Metrics.globalRegistry
}
@EventListener
fun recordServerMetrics(event: WebServerInitializedEvent) {
val webServer = event.webServer
// 记录服务器启动指标
Timer.Sample.start(meterRegistry)
.stop(Timer.builder("server.startup.time")
.description("服务器启动时间")
.register(meterRegistry))
// 记录端口信息
Gauge.builder("server.port")
.description("服务器端口")
.register(meterRegistry) { webServer.port.toDouble() }
}
}
最佳实践总结
关键要点
- 选择合适的服务器:根据应用特性选择最适合的内嵌服务器
- 安全配置:生产环境必须启用 HTTPS
- 性能优化:合理配置连接池、压缩等参数
- 监控日志:完善的访问日志和监控体系
- 环境隔离:不同环境使用不同的配置策略
实用建议
- 开发环境使用 Jetty(启动快)
- 生产环境使用 Undertow(性能好)
- 响应式应用使用 Reactor Netty
- 传统应用继续使用 Tomcat(稳定可靠)
通过合理配置内嵌 Web 服务器,你可以构建出高性能、安全可靠的 Spring Boot 应用。记住,配置不是目的,解决实际问题才是关键! 🎯