一、嵌入式容器架构设计
1. 整体架构概览
图表
代码
复制
下载
全屏
graph TB
A[SpringApplication.run] --> B[创建ApplicationContext]
B --> C[刷新上下文]
C --> D[触发WebServer工厂回调]
D --> E[创建WebServer]
E --> F{端口绑定}
F -->|成功| G[启动容器]
F -->|失败| H[端口递增重试/失败]
G --> I[处理请求]
subgraph "WebServer类型"
J[Tomcat]
K[Jetty]
L[Undertow]
M[Netty]
end
2. 核心接口与抽象
java
复制
下载
// Spring Boot嵌入式容器核心接口
public interface WebServer {
void start() throws WebServerException;
void stop() throws WebServerException;
int getPort();
}
public interface WebServerFactory {
WebServer getWebServer(ServletContextInitializer... initializers);
}
public interface ConfigurableWebServerFactory extends WebServerFactory {
void setPort(int port);
void setAddress(InetAddress address);
void setSsl(Ssl ssl);
void setCompression(Compression compression);
// 更多配置方法...
}
// Servlet容器专用接口
public interface ServletWebServerFactory extends WebServerFactory {
WebServer getWebServer(ServletContextInitializer... initializers);
}
二、容器启动流程详解
1. 启动入口与上下文准备
java
复制
下载
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 1. 创建SpringApplication实例
SpringApplication app = new SpringApplication(Application.class);
// 2. 运行应用(核心启动流程)
ConfigurableApplicationContext context = app.run(args);
// 3. 获取WebServer
WebServer webServer = context.getBean(WebServer.class);
System.out.println("Server started on port: " + webServer.getPort());
}
}
// SpringApplication.run()内部流程
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
// 1. 启动计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2. 准备环境
ConfigurableEnvironment environment = prepareEnvironment();
// 3. 打印Banner
Banner printedBanner = printBanner(environment);
// 4. 创建应用上下文(关键步骤)
ConfigurableApplicationContext context = createApplicationContext();
// 5. 准备上下文
prepareContext(context, environment, args, printedBanner);
// 6. 刷新上下文(触发容器创建)
refreshContext(context);
// 7. 启动完成回调
afterRefresh(context, args);
stopWatch.stop();
return context;
}
// 根据classpath自动选择上下文类型
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根据类路径判断使用哪种Web容器
switch (this.webApplicationType) {
case SERVLET:
// 默认使用AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
} catch (ClassNotFoundException ex) {
throw new IllegalStateException("Unable create a default ApplicationContext", ex);
}
}
return BeanUtils.instantiateClass(contextClass);
}
}
2. WebServer的创建时机
java
复制
下载
// ServletWebServerApplicationContext(关键上下文类)
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
private volatile WebServer webServer;
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建WebServer的核心方法
createWebServer();
} catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 1. 获取WebServer工厂
ServletWebServerFactory factory = getWebServerFactory();
// 2. 获取ServletContextInitializer(用于配置Servlet、Filter等)
ServletContextInitializer[] initializers = getServletContextInitializers();
// 3. 创建WebServer实例
this.webServer = factory.getWebServer(initializers);
} else if (servletContext != null) {
try {
// 外部容器场景
getSelfInitializer().onStartup(servletContext);
} catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
// 4. 初始化PropertySources
initPropertySources();
}
// 获取WebServer工厂(自动检测机制)
protected ServletWebServerFactory getWebServerFactory() {
// 使用Spring的依赖查找
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to missing " +
"ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException(
"Unable to start ServletWebServerApplicationContext due to multiple " +
"ServletWebServerFactory beans: " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
3. Tomcat容器的启动实现
java
复制
下载
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
implements ConfigurableTomcatWebServerFactory {
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 1. 创建Tomcat实例
Tomcat tomcat = new Tomcat();
// 2. 配置基础目录
File baseDir = (this.baseDirectory != null) ? this.baseDirectory
: createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 3. 创建Connector(连接器)
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
// 4. 配置端口和地址
customizeConnector(connector);
// 5. 添加连接器到Service
tomcat.getService().addConnector(connector);
// 6. 自定义Connector
customizeConnector(connector);
tomcat.setConnector(connector);
// 7. 配置引擎和Host
tomcat.getEngine().setBackgroundProcessorDelay(-1);
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
// 8. 准备Tomcat嵌入上下文
prepareContext(tomcat.getHost(), initializers);
// 9. 创建并返回WebServer包装器
return getTomcatWebServer(tomcat);
}
// TomcatWebServer的创建和启动
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
}
// TomcatWebServer包装类
public class TomcatWebServer implements WebServer {
private final Tomcat tomcat;
private final boolean autoStart;
private final GracefulShutdown gracefulShutdown;
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.tomcat = tomcat;
this.autoStart = autoStart;
// 初始化
initialize();
}
private void initialize() throws WebServerException {
// 1. 配置Tomcat服务器
synchronized (this.monitor) {
try {
// 2. 添加引擎
addInstanceIdToEngine();
// 3. 配置上下文(将Spring的ServletContextInitializer适配到Tomcat)
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource())
&& Lifecycle.START_EVENT.equals(event.getType())) {
// 移除Tomcat的默认JSP Servlet
removeServiceConnectors();
}
});
// 4. 启动Tomcat服务器
this.tomcat.start();
// 5. 延迟启动Connector,确保所有Servlet都初始化完成
startDaemonAwaitThread();
} catch (Exception ex) {
stopSilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
@Override
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
// 启动Connector
Connector connector = this.tomcat.getConnector();
if (connector != null) {
// 执行Connector的start方法
connector.getProtocolHandler().start();
}
this.started = true;
logger.info("Tomcat started on port(s): " + getPortsDescription());
} catch (Exception ex) {
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
}
三、端口绑定原理深度分析
1. 端口获取与配置优先级
java
复制
下载
public class WebServerFactoryCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
// 自定义端口配置
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
// 应用所有自定义配置器
LambdaSafe.callbacks(WebServerFactoryCustomizer.class,
getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
// 端口配置的优先级顺序
private void customizePort(ConfigurableWebServerFactory factory) {
// 配置优先级:
// 1. 编程式配置(最高优先级)
// 2. 外部化配置(application.properties)
// 3. 操作系统环境变量
// 4. 默认值(8080)
// 获取端口配置
Integer port = getConfiguredPort();
if (port != null) {
factory.setPort(port);
} else {
// 使用随机端口
if (isRandomPortEnabled()) {
factory.setPort(0); // 0表示随机端口
}
}
// 配置地址绑定
InetAddress address = getConfiguredAddress();
if (address != null) {
factory.setAddress(address);
}
}
}
// 端口配置来源
public class ServerProperties {
private final Integer port;
private final InetAddress address;
private final Ssl ssl;
private final Compression compression;
// 从配置文件中读取
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public static class ServerProperties {
/**
* 端口配置,支持多种格式:
* server.port=8080 # 固定端口
* server.port=0 # 随机端口
* server.port=${PORT:8080} # 环境变量优先
* server.port=${random.int(10000, 20000)} # 随机范围
*/
private Integer port;
/**
* 绑定地址:
* server.address=192.168.1.100 # 绑定到特定IP
* server.address=0.0.0.0 # 绑定到所有接口(默认)
*/
private InetAddress address;
/**
* SSL配置
*/
private Ssl ssl;
// 应用配置到WebServerFactory
public void customize(ConfigurableWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get();
map.from(this::getPort).whenNonNull().to(factory::setPort);
map.from(this::getAddress).whenNonNull().to(factory::setAddress);
map.from(this::getSsl).whenNonNull().to(factory::setSsl);
map.from(this::getCompression).whenNonNull().to(factory::setCompression);
}
}
}
2. 随机端口实现机制
java
复制
下载
public class AbstractServletWebServerFactory
extends AbstractConfigurableWebServerFactory {
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
// 如果端口为0,使用随机端口
if (getPort() == null || getPort() == 0) {
// 使用Socket找到可用端口
int port = SocketUtils.findAvailableTcpPort();
setPort(port);
}
// 创建WebServer...
return createWebServer(initializers);
}
}
// 端口查找工具类
public abstract class SocketUtils {
/**
* 查找可用的TCP端口
* @param minPort 最小端口(包含)
* @param maxPort 最大端口(包含)
* @return 可用的端口号
*/
public static int findAvailableTcpPort(int minPort, int maxPort) {
return findAvailablePort(minPort, maxPort, Type.TCP);
}
/**
* 查找可用端口的实现
*/
private static int findAvailablePort(int minPort, int maxPort, Type type) {
for (int port = minPort; port <= maxPort; port++) {
try {
if (isPortAvailable(port, type)) {
return port;
}
} catch (Exception ex) {
// 端口检查失败,继续尝试下一个
}
}
throw new IllegalStateException(
String.format("Could not find an available %s port in the range [%d, %d]",
type, minPort, maxPort));
}
/**
* 检查端口是否可用
*/
private static boolean isPortAvailable(int port, Type type) {
try (ServerSocket serverSocket = (type == Type.TCP)
? new ServerSocket(port)
: new DatagramSocket(port)) {
// 设置SO_REUSEADDR以快速重用端口
serverSocket.setReuseAddress(true);
// 绑定到本地所有地址
if (type == Type.TCP) {
((ServerSocket) serverSocket).bind(
new InetSocketAddress(InetAddress.getByName("0.0.0.0"), port));
}
return true;
} catch (IOException ex) {
return false;
}
}
}
3. 端口绑定与监听实现
java
复制
下载
// Tomcat Connector端口绑定实现
public class TomcatWebServer extends WebServer {
private void startConnector(Connector connector) {
try {
// 获取协议处理器
ProtocolHandler handler = connector.getProtocolHandler();
// 配置端点
AbstractEndpoint<?, ?> endpoint = (AbstractEndpoint<?, ?>)
handler.getEndpoint();
// 设置端口和地址
endpoint.setPort(connector.getPort());
if (connector.getProperty("address") != null) {
endpoint.setAddress(
InetAddress.getByName(connector.getProperty("address")));
}
// 启动端点,开始监听端口
endpoint.start();
// 端口绑定成功后的回调
connector.setState(LifecycleState.STARTED);
} catch (Exception ex) {
// 端口绑定失败处理
if (connector.getThrowOnFailure()) {
stopSilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
}
// NIO端口的详细绑定过程
public class NioEndpoint extends AbstractEndpoint<NioChannel, SocketChannel> {
@Override
public void bind() throws Exception {
// 1. 初始化ServerSocketChannel
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
// 2. 获取InetSocketAddress
InetSocketAddress addr = new InetSocketAddress(
getAddress(), getPort());
// 3. 绑定到端口
serverSock.socket().bind(addr, getAcceptCount());
// 4. 配置为非阻塞模式
serverSock.configureBlocking(true);
// 5. 设置SO_REUSEADDR
serverSock.socket().setReuseAddress(getReuseAddress());
// 6. 初始化Acceptor线程
initialiseSsl();
// 7. 开始接受连接
startAcceptorThreads();
}
@Override
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// 创建工作线程池
if (getExecutor() == null) {
createExecutor();
}
// 初始化连接限流器
initializeConnectionLatch();
// 启动轮询器线程
startPollers();
// 绑定端口
bind();
// 设置状态为运行中
setState(LifecycleState.STARTING);
}
}
}
四、端口冲突处理与优雅启动
1. 端口冲突检测与重试
java
复制
下载
public class PortInUseException extends WebServerException {
private final int port;
public PortInUseException(int port, Throwable cause) {
super("Port " + port + " is already in use", cause);
this.port = port;
}
public int getPort() {
return this.port;
}
}
// 端口冲突处理器
public class WebServerStartStopLifecycle implements SmartLifecycle {
private final WebServer webServer;
private volatile boolean running;
@Override
public void start() {
this.webServer.start();
this.running = true;
}
@Override
public void stop() {
this.running = false;
try {
this.webServer.stop();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}
// 自动端口递增策略
public class IncrementalPortStrategy implements PortStrategy {
private final int basePort;
private final int maxAttempts;
private final int portRange;
public IncrementalPortStrategy(int basePort, int maxAttempts) {
this.basePort = basePort;
this.maxAttempts = maxAttempts;
this.portRange = 100; // 默认尝试100个端口
}
@Override
public int getNextPort() {
for (int i = 0; i < maxAttempts; i++) {
int port = basePort + (i % portRange);
if (isPortAvailable(port)) {
return port;
}
}
throw new IllegalStateException(
String.format("No available port found in range [%d, %d]",
basePort, basePort + portRange - 1));
}
private boolean isPortAvailable(int port) {
try (ServerSocket serverSocket = new ServerSocket()) {
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(port));
return true;
} catch (IOException e) {
return false;
}
}
}
2. 优雅启动与健康检查
java
复制
下载
// 优雅启动实现
public class GracefulStartup implements ApplicationListener<ApplicationReadyEvent> {
private final HealthIndicator healthIndicator;
private final WebServer webServer;
private boolean ready = false;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
if (!ready) {
// 等待依赖服务就绪
waitForDependencies();
// 执行健康检查
if (performHealthCheck()) {
ready = true;
// 标记服务为可用状态
markAsReady();
}
}
}
private void waitForDependencies() {
// 等待数据库连接就绪
waitForDatabase();
// 等待消息队列连接就绪
waitForMessageQueue();
// 等待缓存服务就绪
waitForCache();
}
private boolean performHealthCheck() {
Health health = healthIndicator.health();
return health.getStatus().equals(Status.UP);
}
}
五、多端口与协议支持
1. HTTP/HTTPS双端口支持
java
复制
下载
@Configuration
public class MultiPortConfiguration {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// 添加HTTPS连接器
factory.addAdditionalTomcatConnectors(createSslConnector());
// 添加HTTP/2支持
factory.addConnectorCustomizers(connector -> {
connector.addUpgradeProtocol(new Http2Protocol());
});
return factory;
}
private Connector createSslConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
// 配置HTTPS
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
try {
// 设置SSL
SSLHostConfig sslHostConfig = new SSLHostConfig();
sslHostConfig.setCertificateKeystoreFile("classpath:keystore.p12");
sslHostConfig.setCertificateKeystorePassword("changeit");
sslHostConfig.setCertificateKeystoreType("PKCS12");
protocol.addSslHostConfig(sslHostConfig);
connector.setScheme("https");
connector.setSecure(true);
connector.setPort(8443); // HTTPS端口
// 配置重定向:HTTP自动跳转到HTTPS
connector.setRedirectPort(8443);
} catch (Exception ex) {
throw new IllegalStateException("Could not configure SSL", ex);
}
return connector;
}
}
2. 管理端口与健康端点分离
yaml
复制
下载
# application.yml配置
server:
port: 8080 # 应用主端口
management:
server:
port: 8081 # 管理端口
address: 127.0.0.1 # 只绑定到本地地址
endpoints:
web:
exposure:
include: health,info,metrics
base-path: /actuator
endpoint:
health:
show-details: always
probes:
enabled: true
java
复制
下载
// 管理端口配置类
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
public class ManagementPortConfiguration {
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
managementPortCustomizer(ManagementServerProperties management) {
return (factory) -> {
if (management.getPort() != null) {
factory.setPort(management.getPort());
}
if (management.getAddress() != null) {
factory.setAddress(management.getAddress());
}
// 只暴露管理端点
factory.setContextPath(management.getBasePath());
factory.setRegisterDefaultServlet(false);
};
}
@Bean
public DispatcherServletPathProvider managementDispatcherServletPathProvider() {
return () -> "/";
}
}
六、性能优化与生产实践
1. 连接器优化配置
java
复制
下载
@Configuration
public class TomcatOptimizationConfig {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
return (factory) -> {
factory.addConnectorCustomizers((connector) -> {
// 获取协议处理器
Http11NioProtocol protocol =
(Http11NioProtocol) connector.getProtocolHandler();
// 连接数配置
protocol.setMaxConnections(10000); // 最大连接数
protocol.setMaxThreads(200); // 最大工作线程
protocol.setMinSpareThreads(20); // 最小空闲线程
protocol.setConnectionTimeout(30000); // 连接超时30秒
// 连接器优化
protocol.setAcceptCount(100); // 等待队列长度
protocol.setMaxKeepAliveRequests(100); // 最大长连接请求数
protocol.setKeepAliveTimeout(30000); // 长连接超时
// NIO缓冲配置
protocol.setSocketBuffer(8192); // Socket缓冲区
protocol.setProcessorCache(200); // 处理器缓存
// 启用SSL会话缓存
protocol.setSSLCacheSize(1024);
protocol.setSSLProtocol("TLSv1.2");
// 启用GZIP压缩
protocol.setCompression("on");
protocol.setCompressionMinSize(2048);
protocol.setCompressableMimeType(
"text/html,text/xml,text/plain,text/css,text/javascript," +
"application/javascript,application/json");
});
// 配置静态资源缓存
factory.addContextCustomizers((context) -> {
context.addWelcomeFile("index.html");
context.setResources(new File("src/main/resources/static"));
context.setCacheMaxSize(1024 * 1024 * 10); // 10MB缓存
});
};
}
}
2. 启动性能监控
java
复制
下载
@Component
public class StartupMetrics implements ApplicationRunner {
private final MeterRegistry meterRegistry;
private final long startupTime;
public StartupMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.startupTime = System.currentTimeMillis();
}
@Override
public void run(ApplicationArguments args) {
// 记录启动时间
long duration = System.currentTimeMillis() - startupTime;
meterRegistry.gauge("application.startup.time", duration);
// 记录内存使用
Runtime runtime = Runtime.getRuntime();
meterRegistry.gauge("jvm.memory.used",
runtime.totalMemory() - runtime.freeMemory());
meterRegistry.gauge("jvm.memory.max", runtime.maxMemory());
// 记录端口信息
WebServer webServer = ApplicationContextHolder.getBean(WebServer.class);
meterRegistry.gauge("server.port", webServer.getPort());
// 记录活跃连接数
Timer timer = Timer.builder("server.connections")
.description("Active connections")
.register(meterRegistry);
// 定期收集连接数
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
int activeConnections = getActiveConnections();
meterRegistry.gauge("server.connections.active", activeConnections);
}, 0, 10, TimeUnit.SECONDS);
}
private int getActiveConnections() {
// 通过JMX获取Tomcat活跃连接数
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
ObjectName objectName = new ObjectName(
"Tomcat:type=ThreadPool,name=\"http-nio-*\"");
Integer connectionCount = (Integer) mBeanServer.getAttribute(
objectName, "connectionCount");
return connectionCount != null ? connectionCount : 0;
} catch (Exception e) {
return 0;
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
七、常见问题与解决方案
1. 端口绑定失败处理
bash
复制
下载
# 常见错误及解决方案
# 错误1: Port 8080 already in use
# 解决方案:
# 1. 杀死占用进程
lsof -i :8080
kill -9 <PID>
# 2. 使用随机端口
server.port=0
# 3. 指定其他端口
server.port=8081
# 错误2: Cannot assign requested address
# 解决方案:
# 1. 检查IP地址配置
server.address=0.0.0.0 # 绑定到所有接口
# 2. 检查网络接口状态
ifconfig
ip addr show
# 错误3: Permission denied (bind)
# 解决方案:
# 1. 使用1024以上端口(非特权端口)
server.port=1025
# 2. 以root权限运行(不推荐)
sudo java -jar app.jar
2. 启动超时配置
yaml
复制
下载
# application.yml
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 优雅关闭超时时间
main:
banner-mode: off # 关闭Banner加快启动
application:
name: myapp
server:
tomcat:
connection-timeout: 20000
keep-alive-timeout: 30000
max-connections: 10000
threads:
max: 200
min-spare: 10
# 启用快速失败
jetty:
max-wait: 10000
min-threads: 8
max-threads: 200
3. 容器选择与性能对比
java
复制
下载
// 容器性能对比配置
@Configuration
public class WebServerComparison {
// Tomcat (默认,平衡性好)
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public ServletWebServerFactory tomcatFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// Tomcat适合传统Web应用
return factory;
}
// Jetty (轻量级,异步支持好)
@Bean
@ConditionalOnClass(name = "org.eclipse.jetty.server.Server")
public ServletWebServerFactory jettyFactory() {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
// Jetty适合高并发长连接
return factory;
}
// Undertow (性能优秀,内存占用低)
@Bean
@ConditionalOnClass(name = "io.undertow.Undertow")
public ServletWebServerFactory undertowFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
// Undertow适合微服务和高性能场景
return factory;
}
// Netty (响应式编程,非阻塞IO)
@Bean
@ConditionalOnClass(name = "reactor.netty.http.server.HttpServer")
public ReactiveWebServerFactory nettyFactory() {
NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory();
// Netty适合响应式应用
return factory;
}
}
八、总结
核心要点总结:
-
启动流程:
-
SpringApplication.run()触发上下文刷新 -
ServletWebServerApplicationContext创建WebServer -
通过SPI自动检测并创建合适的容器(Tomcat/Jetty/Undertow)
-
-
端口绑定机制:
-
端口配置优先级:编程配置 > 外部配置 > 默认值
-
端口0表示随机端口,由SocketUtils查找可用端口
-
实际绑定由容器(如Tomcat的Connector)执行
-
-
关键设计模式:
-
工厂模式:
WebServerFactory创建不同类型的WebServer -
模板方法:
AbstractServletWebServerFactory定义创建流程 -
观察者模式:通过事件监听器处理启动各个阶段
-
-
生产建议:
-
使用随机端口+服务注册实现动态端口分配
-
配置管理端口分离,增强安全性
-
监控启动时间和资源使用
-
根据应用场景选择合适的容器
-
Spring Boot的嵌入式容器设计充分体现了"约定优于配置"的原则,通过自动配置和合理的默认值,让开发者能够快速启动和运行Web应用,同时提供了丰富的扩展点供高级用户定制。