框架面试题20客户端的请求是怎么样传输到控制层的?

客户端请求到达 Spring MVC 控制器的完整流程是一个涉及多层网络协议和框架组件的协作过程。以下是详细步骤分解:


请求传输全流程(共 8 步)


详细步骤解析

步骤 1:客户端发起请求

用户操作:在浏览器输入 URL 或点击链接(如 http://example.com/users
协议封装

GET /users HTTP/1.1
Host: example.com
Accept: application/json

底层传输:浏览器通过操作系统 Socket API 发送 TCP 数据包

步骤 2:网络传输层处理

DNS 解析

浏览器查询 example.com → DNS 服务器返回 IP 地址(如 192.168.1.100

TCP 连接

客户端与服务器建立 TCP 三次握手

客户端 -> SYN -> 服务器
客户端 <- SYN-ACK <- 服务器
客户端 -> ACK -> 服务器

HTTP 封装:请求被封装为 TCP 数据包传输

步骤 3:服务器网络栈处理

网卡接收:服务器物理网卡接收数据帧
内核处理

网络驱动解析以太网帧
IP 层解包获取目标 IP
TCP 层重组数据包

Socket 分发

内核根据目标端口(如 8080)将数据交给监听该端口的进程(Tomcat/Jetty)

步骤 4:Servlet 容器处理

连接器接收(如 Tomcat Coyote):

// Tomcat NIO 伪代码
while (true) {
              
  Socket socket = serverSocket.accept(); // 接受连接
  Executor.execute(new HttpProcessor(socket)); // 交给线程池
}

HTTP 解析

解析请求行(GET /users HTTP/1.1
解析头部(Host, Accept 等)
构造 HttpServletRequest 对象

步骤 5:Spring MVC 前端控制器介入

DispatcherServlet 接管

容器调用 service() 方法

// DispatcherServlet 核心流程
protected void service(HttpServletRequest req, HttpServletResponse resp) {
              
  doDispatch(req, resp); // 开始分发
}

请求分发

查找 HandlerMapping → 确定目标控制器
执行拦截器链(preHandle()

步骤 6:路由到控制器

HandlerMapping 工作

// 简化版映射过程
HandlerExecutionChain chain = handlerMapping.getHandler(request);
if (chain != null) {
              
  // 找到匹配 @GetMapping("/users") 的控制器方法
  controllerMethod = chain.getHandler();
}

参数绑定

解析 @RequestParam, @PathVariable 等注解
反序列化 @RequestBody 内容

步骤 7:控制器执行

方法调用

// 反射调用控制器方法
Object returnValue = method.invoke(
  controllerBean,  // 单例控制器实例
  args              // 绑定后的参数数组
);

业务处理:执行开发者的业务逻辑代码

步骤 8:响应返回

结果处理

@ResponseBody → 使用 HttpMessageConverter 序列化为 JSON
返回视图名称 → ViewResolver 解析为 HTML

响应写出

// 伪代码
response.getWriter().write(json);
response.flushBuffer();

连接关闭:TCP 四次挥手释放连接


关键组件协作图


网络协议栈各层职责

层级 协议/组件 关键职责 示例数据单元
应用层 HTTP 定义请求/响应格式 GET /users HTTP/1.1
传输层 TCP 可靠传输、流量控制 TCP Segment
网络层 IP 路由寻址 IP Packet
数据链路层 Ethernet/WiFi 物理寻址(MAC) Ethernet Frame
物理层 比特流传输 比特(bit)

Spring MVC 核心处理流程代码级解析

// DispatcherServlet 的核心分发方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
            
    // 1. 确定处理器
    HandlerExecutionChain mappedHandler = getHandler(request);
    
    // 2. 确定适配器
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    // 3. 执行拦截器预处理
    if (!mappedHandler.applyPreHandle(request, response)) return;
    
    // 4. 实际调用控制器方法
    ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
    
    // 5. 渲染视图/处理响应
    applyDefaultViewName(request, mv);
    mappedHandler.applyPostHandle(request, response, mv);
    
    // 6. 完成处理(包括异常处理)
    processDispatchResult(request, response, mappedHandler, mv, null);
}

性能优化关键点

TCP 层面

开启 TCP_NODELAY 减少延迟
调整 keepalive 时间

# Tomcat配置
server.tomcat.connection-timeout=20000

Servlet 容器

使用 NIO 模式
优化线程池大小

// Spring Boot配置
server.tomcat.max-threads=250
server.tomcat.accept-count=100

Spring MVC

启用缓存(静态资源)
优化消息转换器

@Configuration
public class WebConfig implements WebMvcConfigurer {
              
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
              
        converters.add(0, new MappingJackson2HttpMessageConverter());
    }
}

常见问题排查

请求未到达控制器

检查 @RequestMapping 路径是否匹配
查看 Tomcat access 日志确认请求到达

400 Bad Request

参数绑定失败(类型不匹配)
@RequestBody JSON 解析错误

404 Not Found

控制器未扫描到(缺少 @ComponentScan
URL 路径拼写错误

线程阻塞

使用 jstack 查看线程栈
检查是否有同步锁竞争

整个流程在优化良好的系统中可在 10-100ms 内完成,其中网络传输通常占 70% 以上时间。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容