一次http请求的步骤
HTTP 请求完整流程详解
文档信息
- 文档类型: 技术知识库
- 创建日期: 2026-01-16
- 适用场景: Java 后端开发、面试准备、技术学习
- 技术栈: HTTP、TCP/IP、Java、Spring Boot、MyBatis
一、概述
本文档详细描述了一个 HTTP 请求从客户端发起到服务器处理再到响应返回的完整流程,特别关注 Java 后端的处理细节。适合后端开发工程师学习和面试准备。
核心流程图
二、客户端发起阶段
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 查询顺序如下:
查询过程:
- 浏览器缓存:检查本地缓存(chrome://net-internals/#dns)
- 操作系统缓存:检查系统 hosts 文件和缓存
- 路由器缓存:检查路由器 DNS 缓存
- ISP DNS:向网络服务商的 DNS 服务器查询
- 递归查询:根域名服务器 → 顶级域名服务器 → 权威域名服务器
2.3 建立 TCP 连接(三次握手)
在获得服务器 IP 地址后,浏览器通过三次握手建立 TCP 连接:
三次握手详解:
- 第一次握手:客户端发送 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 握手:
三、网络传输阶段
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 容器的处理流程:
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 >= #{condition.startTime}
</if>
<if test="condition.endTime != null">
AND created_at <= #{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: 请求图片 → ← 响应图片
所有流共享同一个连接,并行传输
四次挥手(关闭连接):
5.3 浏览器处理响应
浏览器接收到响应后的处理流程:
浏览器缓存策略:
缓存相关响应头:
# 强缓存(不发送请求到服务器)
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 关键流程
- DNS 解析 → 2. TCP 三次握手 → 3. TLS 握手(HTTPS) →
- 发送 HTTP 请求 → 5. Nginx 负载均衡 → 6. Tomcat 接收 →
- Filter 链 → 8. DispatcherServlet → 9. Interceptor →
- Controller 处理 → 11. Service 业务逻辑 → 12. Mapper 数据库查询 →
- 返回响应 → 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 调优经验
- 结合实际项目中遇到的性能问题和优化方案

浙公网安备 33010602011771号