Spring Boot + Undertow 全栈架构深度剖析时序图

18 阅读2分钟

完整的时序图

涵盖从Spring容器启动、Undertow集成、Servlet注册,到用户请求的完整生命周期

sequenceDiagram
    participant U as 用户/浏览器
    participant DNS as DNS服务器
    participant OS as 操作系统<br>内核/网卡
    participant UT as Undertow<br>I/O线程
    participant WT as Undertow<br>工作线程
    participant SCI as ServletContext-<br>Initializer
    participant DS as Dispatcher-<br>Servlet
    participant SC as Spring容器<br>(ApplicationContext)
    participant MVC as Spring MVC<br>组件
    participant DB as 数据库

    Note over U, DB: 阶段1: 应用启动阶段
    rect rgb(240, 255, 240)
        Note over SC, SCI: 1. Spring容器启动
        SC->>SC: SpringApplication.run()<br>创建ApplicationContext
        SC->>SC: 扫描@Component、@Bean等<br>初始化所有单例Bean
        SC->>DS: 创建DispatcherServlet Bean<br>@Bean DispatcherServlet
        
        Note over SC, SCI: 2. 自动配置机制
        SC->>SCI: 创建ServletRegistrationBean<br>(实现ServletContextInitializer)
        SCI->>SCI: 包含DispatcherServlet和URL映射配置
        
        Note over UT, SCI: 3. Undertow服务器启动
        SC->>UT: 创建Undertow实例<br>配置I/O线程(16)和工作线程(512)
        UT->>UT: 绑定端口8080<br>启动事件监听循环
        
        Note over UT, SCI: 4. Servlet注册回调
        UT->>SCI: 回调onStartup()方法<br>(通过TomcatStarter桥接)
        SCI->>UT: 调用servletContext.addServlet()<br>注册DispatcherServlet到Undertow
        UT->>UT: 完成Servlet注册<br>更新URL映射表
    end

    Note over U, DB: 阶段2: DNS解析和网络传输
    rect rgb(240, 248, 255)
        U->>DNS: DNS查询<br>example.com → 192.168.1.100
        DNS->>U: 返回IP地址
        U->>OS: HTTP请求 GET /api/users<br>TCP三次握手建立连接
        OS->>OS: 网卡接收数据→DMA→内核缓冲区
    end

    Note over U, DB: 阶段3: Undertow接收和处理
    rect rgb(255, 248, 240)
        OS->>UT: 通过epoll通知数据就绪<br>I/O线程从内核缓冲区读取
        UT->>UT: 解析HTTP协议<br>创建HttpServerExchange
        UT->>WT: 从线程池(512)借用工作线程<br>传递请求信息
        
        Note over WT, DS: 4. Servlet处理链
        WT->>DS: 调用service()方法<br>请求进入Spring MVC世界
        DS->>MVC: doDispatch()开始处理<br>查找HandlerMapping
        MVC->>MVC: 执行Interceptor.preHandle()<br>参数绑定验证
        MVC->>SC: 调用@Controller方法<br>执行业务逻辑
        SC->>DB: 执行SQL查询<br>工作线程等待数据库响应
        DB->>SC: 返回查询结果
        SC->>MVC: 返回ModelAndView<br>执行Interceptor.postHandle()
        MVC->>DS: 处理完成<br>准备响应数据
        DS->>WT: 返回响应给工作线程
        WT->>UT: 工作线程释放<br>响应数据交还I/O线程
    end

    Note over U, DB: 阶段4: 响应返回
    rect rgb(248, 240, 255)
        UT->>OS: I/O线程写入内核发送缓冲区
        OS->>U: 操作系统组包发送HTTP响应
        U->>U: 浏览器接收响应<br>解析HTML/JSON渲染页面
        UT->>UT: 保持HTTP连接<br>等待下一个请求
    end

    Note over U, DB: 阶段5: 连接管理(后续请求)
    U->>UT: 同一连接的后续请求<br>复用现有TCP连接
    UT->>WT: 直接分配工作线程处理<br>跳过DNS和连接建立阶段


关键环节的详细说明

1. Spring与Undertow的集成细节(阶段1)

自动配置机制

// Spring Boot自动配置类
@Configuration
@ConditionalOnClass({Servlet.class, Undertow.class})
public class UndertowServletWebServerFactoryConfiguration {
    
    @Bean
    public UndertowServletWebServerFactory undertowServletWebServerFactory() {
        // 创建Undertow工厂
        return new UndertowServletWebServerFactory();
    }
}

// Servlet注册(关键桥梁)
@Bean
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration() {
    // 这个Bean实现了ServletContextInitializer接口
    return new ServletRegistrationBean<>(dispatcherServlet(), "/");
}

Undertow启动过程

// Spring Boot启动Undertow的简化流程
public class UndertowWebServer implements WebServer {
    public void start() {
        // 1. 创建Undertow Builder
        Undertow.Builder builder = Undertow.builder()
            .addHttpListener(port, "0.0.0.0")
            .setIoThreads(16)    // I/O线程
            .setWorkerThreads(512) // 工作线程
            .setHandler(handlers);
        
        // 2. 启动服务器
        this.undertow = builder.build();
        this.undertow.start();
        
        // 3. 回调ServletContextInitializer
        for (ServletContextInitializer initializer : initializers) {
            initializer.onStartup(this.getServletContext());
        }
    }
}

2. 请求处理链的详细流程(阶段3)

Undertow → Spring MVC的转换

// Undertow的HttpHandler处理请求
public class ServletInitialHandler implements HttpHandler {
    public void handleRequest(HttpServerExchange exchange) {
        // 1. 将Undertow的Exchange转换为Servlet的Request/Response
        ServletRequestContext servletRequestContext = 
            new ServletRequestContext(exchange, servletContext);
        
        HttpServletRequest request = servletRequestContext.getServletRequest();
        HttpServletResponse response = servletRequestContext.getServletResponse();
        
        // 2. 创建Filter链并执行
        ApplicationFilterChain filterChain = 
            ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
        
        // 3. 最终调用DispatcherServlet.service()
        filterChain.doFilter(request, response);
    }
}

Spring MVC内部处理

// DispatcherServlet的核心处理流程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
    // 1. 查找HandlerMapping
    HandlerExecutionChain mappedHandler = getHandler(request);
    
    // 2. 获取HandlerAdapter
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    // 3. 执行拦截器前置处理
    if (!mappedHandler.applyPreHandle(request, response)) return;
    
    // 4. 实际执行Controller方法
    ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
    
    // 5. 执行拦截器后置处理
    mappedHandler.applyPostHandle(request, response, mv);
    
    // 6. 处理结果渲染
    processDispatchResult(request, response, mappedHandler, mv, null);
}

3. 网络层面的细节(阶段2和4)

DNS解析过程

  1. 浏览器缓存检查 → 2. 系统缓存检查 → 3. 路由器缓存检查 → 4. ISP DNS缓存 → 5. 递归查询 → 6. 返回IP地址

TCP连接复用

  1. 建立连接后,后续请求复用同一连接
  2. 第一次请求: TCP握手 → HTTP请求/响应
  3. 第二次请求: HTTP请求/响应(复用连接)
  4. 第三次请求: HTTP请求/响应(复用连接)

4. 线程模型的关键点

Undertow的两级线程池

  • I/O线程(16个) :非阻塞处理网络I/O,不执行业务逻辑
  • 工作线程(512个) :阻塞式处理业务逻辑,可被挂起等待IO

优势: 工作线程在等待数据库时可以处理其他请求

  • 工作线程1: 开始处理请求A → 等待数据库 → 挂起
  • 工作线程1: 被唤醒处理请求B → 快速计算 → 返回响应
  • 工作线程1: 数据库响应到达 → 继续处理请求A → 返回响应

完整数据流总结

  1. 启动阶段:Spring容器 → 创建Bean → 启动Undertow → 注册Servlet
  2. 请求接收:DNS解析 → 建立TCP连接 → 内核接收 → Undertow I/O线程
  3. 业务处理:工作线程 → DispatcherServlet → Spring MVC → 业务逻辑 → 数据库
  4. 响应返回:处理结果 → 工作线程 → I/O线程 → 内核发送 → 网络传输 → 浏览器