一次 web 请求响应中,通常那个部分最耗时?
文章目录
- 一次Web请求的完整旅程
- 1. DNS解析
- 2. TCP连接建立
- 3. 发送HTTP请求
- 4. 服务器处理
- 5. 服务器响应
- 6. 浏览器渲染
- 哪个环节通常最耗时?
- 1. 数据库查询
- 2. 外部API调用
- 3. 复杂的业务逻辑
- 如何优化各个环节?
- 1. 数据库优化
- 2. 缓存策略
- 3. 异步处理
- 总结
一次Web请求的完整旅程
当用户在浏览器地址栏输入网址并按下回车,到最终看到页面内容,这中间经历了什么?我们来拆解一下这个过程:
1. DNS解析
浏览器首先要把域名转换成IP地址。比如把"www.baidu.com"转换成"120.5.5.46"。这个时间取决于DNS服务器的响应速度、是否有DNS缓存还有良好的网络环境。
2. TCP连接建立
找到IP地址后,浏览器需要和服务器建立TCP连接,这就是三次握手,主要受网络延迟影响。
3. 发送HTTP请求
连接建立后,浏览器发送HTTP请求到服务器,这个过程通常很快,除非请求数据很大。
4. 服务器处理
服务器接收到请求后,开始处理业务逻辑。这通常是最"邪门"的环节。
5. 服务器响应
服务器将处理结果发送回浏览器。主要取决于响应数据的大小和网络带宽。
6. 浏览器渲染
浏览器接收到HTML后,开始解析、渲染页面,时间取决于加载的库、脚本等需要网络的组件的数量的网络消耗。
哪个环节通常最耗时?
答案:通常是服务器处理时间
在大多数情况下,服务器处理时间是最大的性能瓶颈。为什么这么说?
1. 数据库查询
现在的Web应用大多需要查询数据库:
-- 一个复杂的查询可能需要几百毫秒甚至几秒
SELECT u.name, p.title, COUNT(c.id) as comment_count
FROM users u
JOIN posts p ON u.id = p.user_id
LEFT JOIN comments c ON p.id = c.post_id
WHERE u.created_at > '2023-01-01'
GROUP BY u.id, p.id
ORDER BY comment_count DESC
LIMIT 20;
如果没有合适的索引,这样的查询轻松就能花掉几秒钟。
2. 外部API调用
很多应用需要调用第三方服务:
- 支付接口调用
- 地图服务API
- 短信发送服务
每个外部调用都可能增加几百毫秒的延迟,而且还可能失败重试。
3. 复杂的业务逻辑
- 服务之间的调用(RPC)
- 复杂的业务逻辑(电商的价格计算)
- 数据处理(格式转换与序列化)
- 上传大文件、文件压缩(Zip)
- 权限校验(用户身份认证)
- 记录日志(操作日志持久化到磁盘)
这些操作都要销毁大量的时间,耗时比较久。
如何优化各个环节?
1. 数据库优化
-- 给常用查询字段加索引
CREATE INDEX idx_user_id ON posts(user_id);-- 用JOIN替代循环查询
SELECT p.title, u.name
FROM posts p JOIN users u ON p.user_id = u.id;
索引是一种高效的数据结构,可以提高数据的查询效率。
2. 缓存策略
@Service
public class ProductService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate ProductMapper productMapper;public List<Product> getHotProducts() {String cacheKey = "hot_products";// 先查缓存List<Product> cached = (List<Product>) redisTemplate.opsForValue().get(cacheKey);if (cached != null) {return cached;}// 缓存没有,查数据库List<Product> products = productMapper.getHotProducts();// 存入缓存,5分钟过期redisTemplate.opsForValue().set(cacheKey, products, 5, TimeUnit.MINUTES);return products;}
}
热点数据被频繁查询,每次都查数据库会把数据库拖垮。缓存后,100个用户访问只需要查1次数据库,这样不仅提高的数据的查询,还减少了磁盘IO,缩短了时间。
3. 异步处理
@RestController
public class OrderController {@Autowiredprivate OrderService orderService;@Autowiredprivate AsyncTaskService asyncTaskService;@PostMapping("/order")public ResponseEntity<?> createOrder(@RequestBody OrderRequest request) {// 先快速创建订单并响应Order order = orderService.createOrder(request);// 异步处理耗时任务asyncTaskService.sendOrderEmail(order.getId());asyncTaskService.updateInventory(order.getItems());return ResponseEntity.ok(Map.of("orderId", order.getId(), "status", "processing"));}
}@Service
public class AsyncTaskService {@Asyncpublic void sendOrderEmail(Long orderId) {// 发送订单确认邮件(耗时操作)emailService.sendOrderConfirmation(orderId);}@Async public void updateInventory(List<OrderItem> items) {// 更新库存(可能需要调用多个服务)inventoryService.updateStock(items);}
}
主线程专注处理核心业务,耗时操作放到后台慢慢处理,整体吞吐量大大提升。
总结
所以一次Web请求响应中最耗时的大多数情况下是服务器处理时间,特别是数据库查询和外部API调用。