一次http请求的步骤

HTTP 请求完整流程详解

文档信息

  • 文档类型: 技术知识库
  • 创建日期: 2026-01-16
  • 适用场景: Java 后端开发、面试准备、技术学习
  • 技术栈: HTTP、TCP/IP、Java、Spring Boot、MyBatis

一、概述

本文档详细描述了一个 HTTP 请求从客户端发起到服务器处理再到响应返回的完整流程,特别关注 Java 后端的处理细节。适合后端开发工程师学习和面试准备。

核心流程图

sequenceDiagram participant Browser as 浏览器 participant DNS as DNS服务器 participant LB as 负载均衡 participant Tomcat as Tomcat容器 participant Spring as Spring MVC participant Service as 业务层 participant DB as 数据库 Browser->>DNS: 1. 域名解析 DNS-->>Browser: 返回IP地址 Browser->>Browser: 2. 建立TCP连接(三次握手) Browser->>LB: 3. 发送HTTP请求 LB->>Tomcat: 4. 转发请求 Tomcat->>Tomcat: 5. 解析HTTP协议 Tomcat->>Spring: 6. Filter链处理 Spring->>Spring: 7. DispatcherServlet路由 Spring->>Service: 8. 执行业务逻辑 Service->>DB: 9. 数据库查询 DB-->>Service: 返回数据 Service-->>Spring: 10. 返回结果 Spring-->>Tomcat: 11. 响应序列化 Tomcat-->>Browser: 12. 返回HTTP响应

二、客户端发起阶段

2.1 URL 解析

当用户在浏览器输入 URL 或点击链接时,浏览器首先解析 URL 的各个组成部分:

https://example.com:443/api/user?id=123#section

协议: https
域名: example.com
端口: 443 (默认)
路径: /api/user
参数: id=123
锚点: section

2.2 DNS 域名解析

浏览器需要将域名转换为 IP 地址,DNS 查询顺序如下:

graph LR A[浏览器DNS缓存] --> B{找到?} B -->|是| Z[返回IP] B -->|否| C[操作系统DNS缓存] C --> D{找到?} D -->|是| Z D -->|否| E[路由器DNS缓存] E --> F{找到?} F -->|是| Z F -->|否| G[ISP DNS服务器] G --> H[根DNS → 顶级域DNS → 权威DNS] H --> Z

查询过程:

  1. 浏览器缓存:检查本地缓存(chrome://net-internals/#dns)
  2. 操作系统缓存:检查系统 hosts 文件和缓存
  3. 路由器缓存:检查路由器 DNS 缓存
  4. ISP DNS:向网络服务商的 DNS 服务器查询
  5. 递归查询:根域名服务器 → 顶级域名服务器 → 权威域名服务器

2.3 建立 TCP 连接(三次握手)

在获得服务器 IP 地址后,浏览器通过三次握手建立 TCP 连接:

sequenceDiagram participant Client as 客户端 participant Server as 服务器 Note over Client,Server: 第一次握手 Client->>Server: SYN=1, seq=x Note right of Server: SYN_RECEIVED Note over Client,Server: 第二次握手 Server->>Client: SYN=1, ACK=1, seq=y, ack=x+1 Note left of Client: ESTABLISHED Note over Client,Server: 第三次握手 Client->>Server: ACK=1, seq=x+1, ack=y+1 Note right of Server: ESTABLISHED

三次握手详解:

  • 第一次握手:客户端发送 SYN 包(seq=x),进入 SYN_SENT 状态
  • 第二次握手:服务器回应 SYN+ACK 包(seq=y, ack=x+1),进入 SYN_RECEIVED 状态
  • 第三次握手:客户端发送 ACK 包(ack=y+1),双方进入 ESTABLISHED 状态

为什么需要三次握手?

  • 防止旧的重复连接初始化造成混乱
  • 确认双方的发送和接收能力都正常
  • 同步双方的初始序列号

2.4 TLS/SSL 握手(HTTPS)

如果是 HTTPS 请求,还需要进行 TLS/SSL 握手:

sequenceDiagram participant Client as 客户端 participant Server as 服务器 Client->>Server: 1. Client Hello (支持的加密算法) Server->>Client: 2. Server Hello (选择的加密算法) Server->>Client: 3. Certificate (服务器证书) Server->>Client: 4. Server Hello Done Client->>Client: 5. 验证证书 Client->>Server: 6. Client Key Exchange (预主密钥) Client->>Server: 7. Change Cipher Spec Server->>Client: 8. Change Cipher Spec Note over Client,Server: 握手完成,开始加密通信

三、网络传输阶段

3.1 发送 HTTP 请求

浏览器构造 HTTP 请求报文:

GET /api/user?id=123 HTTP/1.1
Host: example.com
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: sessionId=abc123; userId=456
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

请求报文结构:

  • 请求行:方法(GET)、URI(/api/user)、协议版本(HTTP/1.1)
  • 请求头:Host、User-Agent、Cookie、Authorization 等
  • 空行:请求头和请求体的分隔
  • 请求体:POST/PUT 请求的数据(GET 请求无请求体)

3.2 OSI 七层模型传输

HTTP 请求在网络中按照 OSI 模型逐层封装传输:

层级 名称 功能 协议/设备
7 应用层 HTTP 报文 HTTP/HTTPS
6 表示层 数据格式转换、加密 TLS/SSL
5 会话层 建立、管理、终止会话 Session
4 传输层 TCP 分段、端口号 TCP (80/443)
3 网络层 IP 分组、路由选择 IP、路由器
2 数据链路层 封装成帧、MAC 地址 以太网、交换机
1 物理层 比特流传输 网卡、网线

数据封装过程:

HTTP数据 
  → TCP段(添加端口号、序列号)
    → IP包(添加源/目标IP地址)
      → 以太网帧(添加源/目标MAC地址)
        → 比特流(电信号/光信号)

四、Java 后端处理阶段(核心)

4.1 请求到达服务器

请求首先到达前置的 Web 服务器(Nginx/Apache):

# Nginx 配置示例
upstream backend_servers {
    server 192.168.1.101:8080 weight=3;
    server 192.168.1.102:8080 weight=2;
    server 192.168.1.103:8080 weight=1;
}

server {
    listen 80;
    server_name example.com;

    location /api/ {
        proxy_pass http://backend_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Nginx 的主要功能:

  • 负载均衡:将请求分发到多台应用服务器
  • 反向代理:隐藏后端服务器真实 IP
  • 静态资源:直接处理静态文件(图片、CSS、JS)
  • SSL 终止:处理 HTTPS 加密解密
  • 限流防护:限制请求频率,防止攻击

4.2 Servlet 容器处理(Tomcat)

请求进入 Tomcat 容器的处理流程:

graph TD A[Connector接收请求] --> B[Protocol解析HTTP] B --> C[Processor处理协议] C --> D[Adapter适配器] D --> E[Container容器] E --> F[Engine引擎] F --> G[Host虚拟主机] G --> H[Context应用上下文] H --> I[Wrapper Servlet包装器] I --> J[Servlet实例]

Tomcat 核心组件:

// 1. Connector - 监听端口,接收 Socket 连接
Connector connector = new Connector("HTTP/1.1");
connector.setPort(8080);

// 2. 解析 HTTP 协议,封装为 HttpServletRequest
HttpServletRequest request = ...; // 包含请求行、请求头、请求体
HttpServletResponse response = ...; // 用于构建响应

// 3. 查找对应的 Servlet
ServletMapping mapping = findServletMapping(request.getRequestURI());
Servlet servlet = getServlet(mapping.getServletName());

// 4. 调用 Servlet 的 service 方法
servlet.service(request, response);

Tomcat 线程模型:

┌─────────────────────────────────────┐
│  Acceptor 线程(接收连接)            │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│  Worker 线程池(处理请求)            │
│  - maxThreads: 200                  │
│  - minSpareThreads: 10              │
│  - maxConnections: 10000            │
└─────────────────────────────────────┘

4.3 过滤器链(Filter Chain)

请求在到达 Servlet 之前,会经过一系列 Filter:

// Filter 执行顺序(责任链模式)
@WebFilter(urlPatterns = "/*", filterName = "encodingFilter")
@Order(1)
public class CharacterEncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        // 前置处理:设置字符编码
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        
        // 继续执行后续 Filter
        chain.doFilter(request, response);
        
        // 后置处理(可选)
    }
}

@WebFilter(urlPatterns = "/*", filterName = "corsFilter")
@Order(2)
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 跨域配置
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
        
        chain.doFilter(request, response);
    }
}

@WebFilter(urlPatterns = "/*", filterName = "authFilter")
@Order(3)
public class AuthenticationFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        // 验证 Token
        String token = httpRequest.getHeader("Authorization");
        if (!validateToken(token)) {
            ((HttpServletResponse) response).sendError(401, "Unauthorized");
            return;
        }
        
        chain.doFilter(request, response);
    }
    
    private boolean validateToken(String token) {
        // Token 验证逻辑
        return token != null && token.startsWith("Bearer ");
    }
}

常见 Filter 功能:

  • 编码过滤器:统一字符编码(UTF-8)
  • 跨域过滤器:处理 CORS 跨域请求
  • 认证过滤器:验证用户身份(JWT Token)
  • 日志过滤器:记录请求日志
  • 压缩过滤器:响应内容 Gzip 压缩
  • XSS 过滤器:防止跨站脚本攻击

4.4 DispatcherServlet(Spring MVC 核心)

请求进入 Spring MVC 的前端控制器:

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    
    // DispatcherServlet 处理流程
    public ModelAndView handleRequest(HttpServletRequest request, 
                                     HttpServletResponse response) {
        
        // 1. 查找 HandlerMapping - 找到处理请求的 Controller 方法
        HandlerExecutionChain handler = getHandler(request);
        
        // 2. 获取 HandlerAdapter - 执行适配器
        HandlerAdapter adapter = getHandlerAdapter(handler.getHandler());
        
        // 3. 执行拦截器的 preHandle 方法
        if (!handler.applyPreHandle(request, response)) {
            return null;
        }
        
        // 4. 真正执行 Controller 方法
        ModelAndView mv = adapter.handle(request, response, handler.getHandler());
        
        // 5. 执行拦截器的 postHandle 方法
        handler.applyPostHandle(request, response, mv);
        
        // 6. 处理视图渲染
        processDispatchResult(request, response, handler, mv);
        
        // 7. 执行拦截器的 afterCompletion 方法
        handler.triggerAfterCompletion(request, response, null);
        
        return mv;
    }
}

HandlerMapping 路由映射:

@RestController
@RequestMapping("/api")
public class UserController {
    
    // RequestMappingHandlerMapping 会扫描这些注解
    // 建立 URL → Method 的映射关系
    
    @GetMapping("/user")  // GET /api/user
    public Result getUser(@RequestParam Long id) {
        // ...
    }
    
    @PostMapping("/user")  // POST /api/user
    public Result createUser(@RequestBody UserDTO userDTO) {
        // ...
    }
    
    @PutMapping("/user/{id}")  // PUT /api/user/123
    public Result updateUser(@PathVariable Long id, @RequestBody UserDTO userDTO) {
        // ...
    }
    
    @DeleteMapping("/user/{id}")  // DELETE /api/user/123
    public Result deleteUser(@PathVariable Long id) {
        // ...
    }
}

参数解析器(ArgumentResolver):

Spring MVC 会自动解析各种类型的参数:

@RestController
public class ExampleController {
    
    // 1. @RequestParam - 解析查询参数
    @GetMapping("/user")
    public User getUser(@RequestParam Long id) {
        // id 从 ?id=123 中提取
    }
    
    // 2. @PathVariable - 解析路径参数
    @GetMapping("/user/{id}")
    public User getUserById(@PathVariable Long id) {
        // id 从 /user/123 中提取
    }
    
    // 3. @RequestBody - 解析请求体(JSON)
    @PostMapping("/user")
    public Result createUser(@RequestBody UserDTO userDTO) {
        // userDTO 从请求体 JSON 反序列化而来
    }
    
    // 4. @RequestHeader - 解析请求头
    @GetMapping("/info")
    public String getInfo(@RequestHeader("User-Agent") String userAgent) {
        // userAgent 从 Header 中提取
    }
    
    // 5. @CookieValue - 解析 Cookie
    @GetMapping("/session")
    public String getSession(@CookieValue("JSESSIONID") String sessionId) {
        // sessionId 从 Cookie 中提取
    }
    
    // 6. HttpServletRequest - 原始请求对象
    @GetMapping("/raw")
    public void handleRaw(HttpServletRequest request, HttpServletResponse response) {
        String ip = request.getRemoteAddr();
    }
}

参数校验(Validation):

@RestController
@Validated
public class UserController {
    
    @PostMapping("/user")
    public Result createUser(@Valid @RequestBody UserDTO userDTO) {
        // Spring 会自动校验 userDTO 的字段
        return Result.success();
    }
}

@Data
public class UserDTO {
    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
    private String username;
    
    @NotNull(message = "密码不能为空")
    @Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", 
             message = "密码必须包含大小写字母和数字,长度至少8位")
    private String password;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @Min(value = 0, message = "年龄不能小于0")
    @Max(value = 150, message = "年龄不能大于150")
    private Integer age;
}

拦截器(Interceptor):

@Component
public class LoginInterceptor implements HandlerInterceptor {
    
    // 1. 前置处理:Controller 方法执行前
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) throws Exception {
        System.out.println("preHandle: 开始处理请求");
        
        // 检查用户登录状态
        String token = request.getHeader("Authorization");
        if (token == null) {
            response.sendError(401, "Unauthorized");
            return false; // 返回 false 终止请求
        }
        
        return true; // 返回 true 继续执行
    }
    
    // 2. 后置处理:Controller 方法执行后,视图渲染前
    @Override
    public void postHandle(HttpServletRequest request, 
                          HttpServletResponse response, 
                          Object handler, 
                          ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle: Controller 执行完成");
    }
    
    // 3. 完成处理:视图渲染后
    @Override
    public void afterCompletion(HttpServletRequest request, 
                               HttpServletResponse response, 
                               Object handler, 
                               Exception ex) throws Exception {
        System.out.println("afterCompletion: 请求完全处理完成");
        // 记录日志、清理资源
    }
}

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Autowired
    private LoginInterceptor loginInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**")  // 拦截的路径
                .excludePathPatterns("/api/login", "/api/register");  // 排除的路径
    }
}

4.5 业务处理层

请求到达 Controller 后,开始业务逻辑处理:

@RestController
@RequestMapping("/api")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/user")
    public Result<UserVO> getUser(@RequestParam Long id) {
        try {
            // 1. 参数校验
            if (id == null || id <= 0) {
                return Result.fail("用户ID不合法");
            }
            
            // 2. 调用 Service 业务逻辑
            UserInfo userInfo = userService.getUserById(id);
            
            // 3. 数据转换(Entity → VO)
            UserVO userVO = convertToVO(userInfo);
            
            // 4. 返回结果
            return Result.success(userVO);
            
        } catch (Exception e) {
            log.error("获取用户信息失败, userId={}", id, e);
            return Result.fail("系统异常,请稍后重试");
        }
    }
    
    private UserVO convertToVO(UserInfo userInfo) {
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(userInfo, vo);
        return vo;
    }
}

Service 层处理:

@Service
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserInfoMapper userInfoMapper;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Override
    public UserInfo getUserById(Long id) {
        // 1. 尝试从 Redis 缓存获取
        String cacheKey = "user:info:" + id;
        UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(cacheKey);
        
        if (userInfo != null) {
            log.info("从缓存获取用户信息, userId={}", id);
            return userInfo;
        }
        
        // 2. 缓存未命中,从数据库查询
        userInfo = userInfoMapper.selectById(id);
        
        if (userInfo == null) {
            throw new BusinessException("用户不存在");
        }
        
        // 3. 查询扩展信息(如需要)
        // UserInfoExtend userInfoExtend = userInfoApi.insideGetExtendById(id);
        
        // 4. 写入缓存
        redisTemplate.opsForValue().set(cacheKey, userInfo, 30, TimeUnit.MINUTES);
        
        return userInfo;
    }
    
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateUser(UserInfo userInfo) {
        // 1. 更新数据库
        userInfoMapper.updateById(userInfo);
        
        // 2. 删除缓存
        String cacheKey = "user:info:" + userInfo.getId();
        redisTemplate.delete(cacheKey);
        
        // 3. 发送消息通知其他服务
        // mqProvider.sendUserUpdateMessage(userInfo);
    }
}

批量查询优化(避免 N+1 问题):

@Service
public class OrderServiceImpl implements OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @Autowired
    private UserInfoApi userInfoApi;
    
    @Override
    public List<OrderVO> getOrderList(Long userId) {
        // 1. 查询订单列表
        List<Order> orderList = orderMapper.selectByUserId(userId);
        
        // ❌ 错误做法:循环中查询用户信息(N+1 问题)
        // for (Order order : orderList) {
        //     UserInfo user = userInfoApi.getUserInfoById(order.getUserId());
        //     // ...
        // }
        
        // ✅ 正确做法:批量查询
        // 2. 提取所有用户ID
        List<Long> userIdList = orderList.stream()
                .map(Order::getUserId)
                .distinct()
                .collect(Collectors.toList());
        
        // 3. 批量查询用户信息
        List<UserInfo> userInfoList = userInfoApi.getUserInfoListByIds(userIdList);
        
        // 4. 转换为 Map 便于后续查找
        Map<Long, UserInfo> userInfoMap = userInfoList.stream()
                .collect(Collectors.toMap(UserInfo::getId, u -> u));
        
        // 5. 组装返回数据
        return orderList.stream().map(order -> {
            OrderVO vo = new OrderVO();
            BeanUtils.copyProperties(order, vo);
            
            // 从 Map 中获取用户信息
            UserInfo userInfo = userInfoMap.get(order.getUserId());
            if (userInfo != null) {
                vo.setUserName(userInfo.getName());
                vo.setUserAvatar(userInfo.getAvatar());
            }
            
            return vo;
        }).collect(Collectors.toList());
    }
}

4.6 数据访问层

Service 层调用 MyBatis/JPA 执行数据库操作:

@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
    
    // 1. 简单查询(MyBatis-Plus 提供)
    // selectById(Long id)
    // selectList(Wrapper<UserInfo> wrapper)
    
    // 2. 自定义 SQL 查询
    @Select("SELECT * FROM user_info WHERE id = #{id}")
    UserInfo selectById(@Param("id") Long id);
    
    // 3. 复杂查询(使用 XML 配置)
    List<UserInfo> selectByCondition(@Param("condition") UserQueryDTO condition);
}

MyBatis XML 配置:

<!-- UserInfoMapper.xml -->
<mapper namespace="com.vvgame.user.mapper.UserInfoMapper">
    
    <resultMap id="UserInfoMap" type="com.vvgame.user.entity.UserInfo">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="avatar" property="avatar"/>
        <result column="created_at" property="createdAt"/>
    </resultMap>
    
    <select id="selectByCondition" resultMap="UserInfoMap">
        SELECT * FROM user_info
        <where>
            <if test="condition.name != null and condition.name != ''">
                AND name LIKE CONCAT('%', #{condition.name}, '%')
            </if>
            <if test="condition.status != null">
                AND status = #{condition.status}
            </if>
            <if test="condition.startTime != null">
                AND created_at &gt;= #{condition.startTime}
            </if>
            <if test="condition.endTime != null">
                AND created_at &lt;= #{condition.endTime}
            </if>
        </where>
        ORDER BY created_at DESC
        LIMIT #{condition.offset}, #{condition.limit}
    </select>
    
</mapper>

数据库连接池(HikariCP):

# application.yml
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/party_user?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: password
    hikari:
      minimum-idle: 10           # 最小空闲连接数
      maximum-pool-size: 50      # 最大连接池大小
      connection-timeout: 30000  # 连接超时时间(毫秒)
      idle-timeout: 600000       # 空闲连接超时时间(10分钟)
      max-lifetime: 1800000      # 连接最大存活时间(30分钟)
      auto-commit: true          # 自动提交

数据库执行流程:

// HikariCP 连接池处理流程
public UserInfo queryUserFromDB(Long id) {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    
    try {
        // 1. 从连接池获取连接
        conn = dataSource.getConnection();
        
        // 2. 创建 PreparedStatement(防止 SQL 注入)
        String sql = "SELECT * FROM user_info WHERE id = ?";
        stmt = conn.prepareStatement(sql);
        stmt.setLong(1, id);
        
        // 3. 执行查询
        rs = stmt.executeQuery();
        
        // 4. 结果集映射为 Java 对象
        if (rs.next()) {
            UserInfo userInfo = new UserInfo();
            userInfo.setId(rs.getLong("id"));
            userInfo.setName(rs.getString("name"));
            userInfo.setAvatar(rs.getString("avatar"));
            userInfo.setCreatedAt(rs.getTimestamp("created_at"));
            return userInfo;
        }
        
    } catch (SQLException e) {
        log.error("数据库查询失败", e);
        throw new DataAccessException("数据库查询失败", e);
        
    } finally {
        // 5. 释放资源
        closeResource(rs, stmt, conn);
    }
    
    return null;
}

private void closeResource(ResultSet rs, PreparedStatement stmt, Connection conn) {
    try {
        if (rs != null) rs.close();
        if (stmt != null) stmt.close();
        if (conn != null) conn.close(); // 归还连接到连接池
    } catch (SQLException e) {
        log.error("关闭资源失败", e);
    }
}

4.7 响应处理

业务逻辑执行完成后,构造响应:

@RestController
public class UserController {
    
    @GetMapping("/api/user")
    public Result<UserVO> getUser(@RequestParam Long id) {
        UserInfo userInfo = userService.getUserById(id);
        UserVO userVO = convertToVO(userInfo);
        
        // 返回统一响应格式
        return Result.success(userVO);
    }
}

// 统一响应对象
@Data
public class Result<T> {
    private Integer code;      // 业务状态码
    private String message;    // 提示信息
    private T data;           // 返回数据
    private Long timestamp;   // 时间戳
    
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage("success");
        result.setData(data);
        result.setTimestamp(System.currentTimeMillis());
        return result;
    }
    
    public static <T> Result<T> fail(String message) {
        Result<T> result = new Result<>();
        result.setCode(500);
        result.setMessage(message);
        result.setTimestamp(System.currentTimeMillis());
        return result;
    }
}

JSON 序列化(Jackson):

Spring Boot 默认使用 Jackson 将 Java 对象转换为 JSON:

// HttpMessageConverter 将 Result 对象转换为 JSON
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 配置 JSON 转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        
        ObjectMapper objectMapper = new ObjectMapper();
        // 日期格式化
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 忽略 null 值字段
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 驼峰转下划线
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        
        converter.setObjectMapper(objectMapper);
        converters.add(0, converter);
    }
}

设置响应头:

@GetMapping("/api/user")
public ResponseEntity<Result<UserVO>> getUser(@RequestParam Long id) {
    UserVO userVO = userService.getUserById(id);
    
    return ResponseEntity.ok()
            .header("Content-Type", "application/json;charset=UTF-8")
            .header("Cache-Control", "no-cache, no-store, must-revalidate")
            .header("X-Request-Id", UUID.randomUUID().toString())
            .body(Result.success(userVO));
}

五、响应返回阶段

5.1 HTTP 响应报文

服务器构造 HTTP 响应报文返回给客户端:

HTTP/1.1 200 OK
Date: Thu, 16 Jan 2026 08:00:00 GMT
Server: nginx/1.20.1
Content-Type: application/json;charset=UTF-8
Content-Length: 256
Connection: keep-alive
Cache-Control: no-cache, no-store, must-revalidate
X-Request-Id: 123e4567-e89b-12d3-a456-426614174000

{
  "code": 200,
  "message": "success",
  "data": {
    "id": 123,
    "name": "张三",
    "avatar": "https://example.com/avatar/123.jpg",
    "createdAt": "2026-01-15 10:30:00"
  },
  "timestamp": 1705392000000
}

响应报文结构:

  • 状态行:协议版本、状态码(200)、状态描述(OK)
  • 响应头:Content-Type、Content-Length、Cache-Control 等
  • 空行:响应头和响应体的分隔
  • 响应体:JSON 数据

常见 HTTP 状态码:

状态码 含义 说明
200 OK 请求成功
201 Created 资源创建成功
204 No Content 请求成功但无返回内容
301 Moved Permanently 永久重定向
302 Found 临时重定向
304 Not Modified 资源未修改,使用缓存
400 Bad Request 请求参数错误
401 Unauthorized 未授权,需要登录
403 Forbidden 禁止访问
404 Not Found 资源不存在
405 Method Not Allowed 请求方法不允许
500 Internal Server Error 服务器内部错误
502 Bad Gateway 网关错误
503 Service Unavailable 服务不可用
504 Gateway Timeout 网关超时

5.2 TCP 连接管理

短连接(HTTP/1.0):

客户端 → 请求 → 服务器
服务器 → 响应 → 客户端
关闭连接(四次挥手)

长连接(HTTP/1.1):

客户端 ← → 服务器  (建立连接)
请求1 → ← 响应1
请求2 → ← 响应2
请求3 → ← 响应3
...
关闭连接(超时或主动关闭)

HTTP/2 多路复用:

        客户端 ←→ 服务器(一个TCP连接)
        
Stream 1: 请求HTML → ← 响应HTML
Stream 2: 请求CSS  → ← 响应CSS
Stream 3: 请求JS   → ← 响应JS
Stream 4: 请求图片 → ← 响应图片

所有流共享同一个连接,并行传输

四次挥手(关闭连接):

sequenceDiagram participant Client as 客户端 participant Server as 服务器 Note over Client,Server: 第一次挥手 Client->>Server: FIN=1, seq=u Note left of Client: FIN_WAIT_1 Note over Client,Server: 第二次挥手 Server->>Client: ACK=1, seq=v, ack=u+1 Note left of Client: FIN_WAIT_2 Note right of Server: CLOSE_WAIT Note over Client,Server: 第三次挥手 Server->>Client: FIN=1, ACK=1, seq=w, ack=u+1 Note right of Server: LAST_ACK Note over Client,Server: 第四次挥手 Client->>Server: ACK=1, seq=u+1, ack=w+1 Note left of Client: TIME_WAIT (2MSL) Note right of Server: CLOSED Note left of Client: CLOSED

5.3 浏览器处理响应

浏览器接收到响应后的处理流程:

graph TD A[接收HTTP响应] --> B{状态码检查} B -->|2xx 成功| C[解析响应体] B -->|3xx 重定向| D[跳转到新URL] B -->|4xx 客户端错误| E[显示错误信息] B -->|5xx 服务器错误| E C --> F{Content-Type?} F -->|text/html| G[解析HTML] F -->|application/json| H[解析JSON] F -->|image/*| I[显示图片] G --> J[构建DOM树] J --> K[构建CSSOM树] K --> L[合并为渲染树] L --> M[布局Layout] M --> N[绘制Paint] H --> O[执行JavaScript] O --> P[更新页面]

浏览器缓存策略:

graph TD A[浏览器发起请求] --> B{检查强缓存} B -->|Cache-Control/Expires 未过期| C[直接使用缓存] B -->|已过期| D{检查协商缓存} D -->|发送 If-None-Match| E{服务器验证 ETag} D -->|发送 If-Modified-Since| F{服务器验证 Last-Modified} E -->|304 Not Modified| G[使用缓存] E -->|200 OK| H[下载新资源] F -->|304 Not Modified| G F -->|200 OK| H H --> I[更新缓存]

缓存相关响应头:

# 强缓存(不发送请求到服务器)
Cache-Control: max-age=3600          # 缓存1小时
Cache-Control: no-cache              # 每次都需要验证
Cache-Control: no-store              # 不缓存
Expires: Thu, 16 Jan 2026 09:00:00 GMT  # 过期时间(HTTP/1.0)

# 协商缓存(需要向服务器验证)
ETag: "5d8c72a5edda8d6a:3239"       # 资源唯一标识
Last-Modified: Thu, 15 Jan 2026 08:00:00 GMT  # 最后修改时间

# 请求头(浏览器发送)
If-None-Match: "5d8c72a5edda8d6a:3239"  # 上次的 ETag
If-Modified-Since: Thu, 15 Jan 2026 08:00:00 GMT  # 上次修改时间

六、性能优化与最佳实践

6.1 前端优化

1. 减少 HTTP 请求:

  • 合并 CSS/JS 文件
  • 使用 CSS Sprites(雪碧图)
  • 内联小文件(Base64)
  • 懒加载(Lazy Loading)

2. 资源压缩:

# Nginx Gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;
gzip_comp_level 6;

3. CDN 加速:

<!-- 静态资源使用 CDN -->
<link rel="stylesheet" href="https://cdn.example.com/css/style.css">
<script src="https://cdn.example.com/js/app.js"></script>

4. 浏览器缓存:

# Nginx 缓存配置
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

6.2 后端优化

1. 数据库优化:

-- 创建索引
CREATE INDEX idx_user_name ON user_info(name);
CREATE INDEX idx_created_at ON user_info(created_at);

-- 复合索引
CREATE INDEX idx_user_status_time ON user_info(status, created_at);

-- 分析查询性能
EXPLAIN SELECT * FROM user_info WHERE name = '张三';

-- 避免 SELECT *
SELECT id, name, avatar FROM user_info WHERE id = 123;

-- 使用 LIMIT 分页
SELECT * FROM user_info ORDER BY created_at DESC LIMIT 0, 20;

2. Redis 缓存:

@Service
public class UserCacheService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 缓存穿透:使用空对象占位
    public UserInfo getUserWithCache(Long id) {
        String cacheKey = "user:info:" + id;
        
        // 1. 查询缓存
        UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(cacheKey);
        if (userInfo != null) {
            return userInfo;
        }
        
        // 2. 查询数据库
        userInfo = userInfoMapper.selectById(id);
        
        if (userInfo == null) {
            // 缓存空对象,防止缓存穿透
            redisTemplate.opsForValue().set(cacheKey, new UserInfo(), 5, TimeUnit.MINUTES);
            return null;
        }
        
        // 3. 写入缓存
        redisTemplate.opsForValue().set(cacheKey, userInfo, 30, TimeUnit.MINUTES);
        return userInfo;
    }
    
    // 缓存雪崩:随机过期时间
    public void setCacheWithRandomExpire(String key, Object value) {
        int expireTime = 30 + new Random().nextInt(10); // 30-40分钟
        redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.MINUTES);
    }
    
    // 缓存击穿:分布式锁
    public UserInfo getUserWithLock(Long id) {
        String cacheKey = "user:info:" + id;
        String lockKey = "lock:user:" + id;
        
        UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(cacheKey);
        if (userInfo != null) {
            return userInfo;
        }
        
        // 获取分布式锁
        Boolean lockResult = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(lockResult)) {
            try {
                // 双重检查
                userInfo = (UserInfo) redisTemplate.opsForValue().get(cacheKey);
                if (userInfo != null) {
                    return userInfo;
                }
                
                // 查询数据库
                userInfo = userInfoMapper.selectById(id);
                
                // 写入缓存
                if (userInfo != null) {
                    redisTemplate.opsForValue().set(cacheKey, userInfo, 30, TimeUnit.MINUTES);
                }
                
            } finally {
                // 释放锁
                redisTemplate.delete(lockKey);
            }
        } else {
            // 未获取到锁,等待后重试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return getUserWithLock(id);
        }
        
        return userInfo;
    }
}

3. 异步处理:

@Configuration
@EnableAsync
public class AsyncConfig {
    
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("async-");
        executor.initialize();
        return executor;
    }
}

@Service
public class NotificationService {
    
    // 异步发送通知
    @Async
    public void sendNotification(Long userId, String message) {
        // 发送站内信、短信、邮件等
        log.info("发送通知给用户: {}, 内容: {}", userId, message);
    }
}

@RestController
public class OrderController {
    
    @Autowired
    private NotificationService notificationService;
    
    @PostMapping("/api/order")
    public Result createOrder(@RequestBody OrderDTO orderDTO) {
        // 1. 创建订单(同步)
        Order order = orderService.createOrder(orderDTO);
        
        // 2. 发送通知(异步,不阻塞响应)
        notificationService.sendNotification(orderDTO.getUserId(), "订单创建成功");
        
        // 3. 立即返回响应
        return Result.success(order);
    }
}

4. 连接池优化:

# HikariCP 优化配置
spring:
  datasource:
    hikari:
      minimum-idle: 10
      maximum-pool-size: 50
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      # 连接测试查询
      connection-test-query: SELECT 1
      # 泄漏检测阈值
      leak-detection-threshold: 60000

5. 限流熔断(Sentinel):

@RestController
public class ApiController {
    
    // 限流:每秒最多10个请求
    @SentinelResource(value = "getUser", 
                     blockHandler = "handleBlock",
                     fallback = "handleFallback")
    @GetMapping("/api/user")
    public Result getUser(@RequestParam Long id) {
        return Result.success(userService.getUserById(id));
    }
    
    // 限流降级处理
    public Result handleBlock(Long id, BlockException e) {
        return Result.fail("系统繁忙,请稍后重试");
    }
    
    // 异常降级处理
    public Result handleFallback(Long id, Throwable e) {
        log.error("查询用户失败", e);
        return Result.fail("系统异常,请稍后重试");
    }
}

6.3 网络优化

1. HTTP/2:

  • 多路复用:一个连接并行传输多个请求
  • 服务器推送:主动推送相关资源
  • 头部压缩:HPACK 算法压缩 Header

2. Keep-Alive:

# Nginx 长连接配置
keepalive_timeout 65;
keepalive_requests 100;

3. 负载均衡:

upstream backend {
    # 轮询
    server 192.168.1.101:8080;
    server 192.168.1.102:8080;
    
    # 加权轮询
    server 192.168.1.103:8080 weight=3;
    
    # IP Hash
    ip_hash;
    
    # 最少连接
    least_conn;
}

6.4 监控与排查

1. 日志记录:

@Slf4j
@RestController
public class UserController {
    
    @GetMapping("/api/user")
    public Result getUser(@RequestParam Long id, HttpServletRequest request) {
        // 记录请求日志
        log.info("获取用户信息, userId={}, ip={}, userAgent={}", 
                 id, 
                 request.getRemoteAddr(), 
                 request.getHeader("User-Agent"));
        
        long startTime = System.currentTimeMillis();
        
        try {
            UserInfo userInfo = userService.getUserById(id);
            
            // 记录响应时间
            long duration = System.currentTimeMillis() - startTime;
            log.info("获取用户信息成功, userId={}, duration={}ms", id, duration);
            
            return Result.success(userInfo);
            
        } catch (Exception e) {
            // 记录异常日志
            log.error("获取用户信息失败, userId={}", id, e);
            return Result.fail("系统异常");
        }
    }
}

2. 链路追踪(Skywalking):

# application.yml
skywalking:
  agent:
    service-name: party-user
    collector:
      backend-service: localhost:11800

3. 性能监控(Micrometer + Prometheus):

@Configuration
public class MetricsConfig {
    
    @Bean
    public TimedAspect timedAspect(MeterRegistry registry) {
        return new TimedAspect(registry);
    }
}

@RestController
public class UserController {
    
    @Timed(value = "api.user.get", description = "获取用户信息")
    @GetMapping("/api/user")
    public Result getUser(@RequestParam Long id) {
        return Result.success(userService.getUserById(id));
    }
}

4. JVM 监控:

# JVM 参数配置
-Xms2g                    # 初始堆内存
-Xmx2g                    # 最大堆内存
-XX:+UseG1GC              # 使用 G1 垃圾回收器
-XX:MaxGCPauseMillis=200  # 最大GC停顿时间
-XX:+HeapDumpOnOutOfMemoryError  # OOM时生成堆转储
-XX:HeapDumpPath=/tmp/heapdump.hprof

# 监控命令
jps                       # 查看Java进程
jstat -gc <pid> 1000      # 每秒输出GC统计
jmap -heap <pid>          # 查看堆内存使用情况
jstack <pid>              # 查看线程堆栈

七、安全防护

7.1 SQL 注入防护

// ❌ 错误:字符串拼接SQL(存在注入风险)
String sql = "SELECT * FROM user_info WHERE name = '" + name + "'";

// ✅ 正确:使用 PreparedStatement
String sql = "SELECT * FROM user_info WHERE name = ?";
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, name);

// ✅ 正确:使用 MyBatis 参数绑定
@Select("SELECT * FROM user_info WHERE name = #{name}")
UserInfo selectByName(@Param("name") String name);

7.2 XSS 防护

// 转义特殊字符
public String escapeHtml(String input) {
    return StringEscapeUtils.escapeHtml4(input);
}

// 前端渲染时转义
<div th:text="${userInput}"></div>  <!-- Thymeleaf 自动转义 -->
<div v-text="userInput"></div>      <!-- Vue 自动转义 -->

7.3 CSRF 防护

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}

7.4 接口鉴权

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain chain) throws ServletException, IOException {
        
        String token = request.getHeader("Authorization");
        
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
            
            try {
                // 验证 JWT Token
                Claims claims = Jwts.parser()
                        .setSigningKey(SECRET_KEY)
                        .parseClaimsJws(token)
                        .getBody();
                
                String userId = claims.getSubject();
                
                // 设置认证信息到 SecurityContext
                UsernamePasswordAuthenticationToken authentication = 
                    new UsernamePasswordAuthenticationToken(userId, null, Collections.emptyList());
                SecurityContextHolder.getContext().setAuthentication(authentication);
                
            } catch (JwtException e) {
                response.sendError(401, "Invalid token");
                return;
            }
        }
        
        chain.doFilter(request, response);
    }
}

八、面试要点总结

8.1 关键流程

  1. DNS 解析 → 2. TCP 三次握手 → 3. TLS 握手(HTTPS)
  2. 发送 HTTP 请求 → 5. Nginx 负载均衡 → 6. Tomcat 接收
  3. Filter 链 → 8. DispatcherServlet → 9. Interceptor
  4. Controller 处理 → 11. Service 业务逻辑 → 12. Mapper 数据库查询
  5. 返回响应 → 14. 浏览器渲染

8.2 高频考点

考点 关键知识点
TCP 三次握手 SYN、ACK、序列号同步
HTTP 状态码 200/301/302/304/400/401/404/500/502/503
Spring MVC 流程 DispatcherServlet → HandlerMapping → Controller
过滤器 vs 拦截器 Filter(Servlet)、Interceptor(Spring)
数据库连接池 HikariCP、最小/最大连接数、超时时间
缓存策略 Redis、强缓存、协商缓存
性能优化 索引、批量查询、异步处理、限流
安全防护 SQL 注入、XSS、CSRF、JWT

8.3 加分项

  • 提及 HTTP/2 多路复用
  • 说明缓存穿透/雪崩/击穿的解决方案
  • 提到分布式链路追踪(Skywalking/Zipkin)
  • 讲解 JVM 调优经验
  • 结合实际项目中遇到的性能问题和优化方案

九、参考资料

技术文档

网络协议

性能优化

posted @ 2026-01-16 09:59  菜鸟~风  阅读(1)  评论(0)    收藏  举报