http 实现
浏览器作为Web客户端,负责与服务器进行 HTTP 通信,并实现高效的缓存机制。本文深入探讨浏览器如何处理 HTTP 请求和响应,以及如何利用缓存提高性能。
HTTP 协议基础
HTTP(超文本传输协议)是Web的基础协议,浏览器通过HTTP请求资源并接收服务器响应。
HTTP 版本演进
HTTP/1.0
- 每个请求/响应都需要建立新的TCP连接
- 支持基本的头部字段
- 无法复用连接
HTTP/1.1
- 引入持久连接(keep-alive)
- 引入管道化请求(pipelining)
- 新增多种头部字段
- 支持分块传输编码
HTTP/2
- 多路复用(multiplexing)
- 头部压缩(HPACK)
- 服务器推送
- 二进制分帧层
HTTP/3
- 基于QUIC协议(基于UDP)
- 减少连接建立时间
- 改进拥塞控制
- 连接迁移
浏览器如何发起HTTP请求
构建请求对象
- 设置方法(GET、POST等)
- 添加请求头
- 准备请求体(如适用)
DNS解析
- 检查缓存
- 递归查询DNS服务器
- 解析域名为IP地址
建立连接
- 创建TCP连接(HTTP/1.x和HTTP/2)
- 或创建QUIC连接(HTTP/3)
- 进行TLS握手(如使用HTTPS)
发送请求
- 传输HTTP消息头
- 传输HTTP消息体(如适用)
接收响应
- 解析状态行
- 处理响应头
- 处理响应体
HTTP请求方法
浏览器支持多种HTTP方法,每种适用于不同场景:
方法 | 描述 | 典型用途 |
---|---|---|
GET | 请求指定资源 | 获取网页、图片、脚本等 |
POST | 提交数据 | 表单提交、文件上传 |
HEAD | 仅请求头信息 | 检查资源是否存在、校验缓存 |
PUT | 上传资源 | RESTful API中的资源更新 |
DELETE | 删除资源 | RESTful API中的资源删除 |
OPTIONS | 获取支持的方法 | CORS预检请求、API探测 |
PATCH | 部分更新资源 | RESTful API中的局部更新 |
HTTP状态码
浏览器根据响应状态码执行不同操作:
- 1xx:信息性响应(请求已接收,继续处理)
- 2xx:成功(请求已成功接收、理解和处理)
- 3xx:重定向(需要进一步操作才能完成请求)
- 4xx:客户端错误(请求包含错误或无法完成)
- 5xx:服务器错误(服务器处理请求时出错)
常见状态码及浏览器行为:
状态码 | 描述 | 浏览器行为 |
---|---|---|
200 | OK | 正常显示响应内容 |
301 | 永久重定向 | 缓存新地址并重定向 |
302 | 临时重定向 | 重定向但不缓存 |
304 | Not Modified | 使用本地缓存 |
404 | Not Found | 显示错误页面 |
500 | Server Error | 显示错误页面 |
浏览器缓存机制
浏览器缓存是提高Web性能的重要机制,通过重用之前获取的资源,减少网络请求。
缓存位置
浏览器缓存按优先级排序:
内存缓存
- 存储在RAM中
- 访问速度最快
- 生命周期短(关闭标签页或浏览器后清除)
- 通常用于当前会话使用的资源
磁盘缓存
- 存储在硬盘上
- 持久化存储
- 容量比内存缓存大
- 可以在浏览器会话之间保持
Service Worker缓存
- 通过JavaScript控制的缓存
- 可编程性强
- 支持离线访问
- 独立于浏览器缓存
Push缓存
- HTTP/2服务器推送资源的缓存
- 生命周期很短
- 一般仅在会话中有效
缓存策略
浏览器实现两种主要的缓存策略:
强缓存
不需要向服务器发送请求,直接从缓存读取资源。通过以下头部控制:
Expires(HTTP/1.0)
Expires: Wed, 21 Oct 2023 07:28:00 GMT
- 指定资源过期的绝对时间
- 依赖客户端时间,可能不准确
- 优先级低于Cache-Control
Cache-Control(HTTP/1.1)
Cache-Control: max-age=31536000
- 指定资源有效期的相对时间(秒)
- 更精确,不依赖客户端时间
- 常用指令:
max-age
:缓存有效时间no-cache
:强制验证缓存no-store
:禁止缓存private
:仅客户端缓存public
:允许中间缓存
协商缓存
当强缓存失效时,浏览器向服务器发送请求,服务器根据请求头判断是否使用缓存:
Last-Modified / If-Modified-Since
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT
- 基于资源的最后修改时间
- 精度为秒级
- 不适用于频繁变化的资源
ETag / If-None-Match
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
- 基于资源内容生成的唯一标识符
- 更精确,不依赖时间
- 计算ETag会消耗服务器资源
缓存流程
浏览器处理缓存的完整流程:
检查是否有强缓存命中
- 解析Cache-Control/Expires头
- 如命中,直接使用缓存资源(不发请求)
- 如未命中,进入协商缓存阶段
协商缓存检查
- 向服务器发送带有If-Modified-Since或If-None-Match头的请求
- 服务器比较这些头与当前资源状态
- 如资源未变化,返回304 Not Modified(不返回资源内容)
- 如资源已变化,返回200 OK和新资源
缓存响应
- 对于200响应,根据响应头更新缓存
- 对于304响应,更新缓存元数据(如过期时间)
缓存控制指令详解
服务器响应头
Cache-Control: max-age=3600, must-revalidate, public
Expires: Wed, 21 Oct 2023 07:28:00 GMT
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
Vary: Accept-Encoding, User-Agent
- must-revalidate:过期缓存必须验证才能使用
- public:任何缓存都可以存储响应
- private:只有浏览器可以缓存,中间代理不可缓存
- no-cache:每次使用前必须验证缓存
- no-store:完全禁止缓存
- Vary:指定根据哪些请求头字段变化导致响应变化
客户端请求头
Cache-Control: max-age=0
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT
- max-age=0:强制验证缓存
- no-cache:忽略本地缓存,强制验证
- no-store:禁止存储响应
按资源类型的缓存策略
不同类型的资源适用不同的缓存策略:
HTML文档
- 短期缓存或不缓存
- 使用ETag进行验证
Cache-Control: no-cache
CSS和JavaScript
- 长期缓存
- 使用版本号或哈希值在文件名中
Cache-Control: max-age=31536000, immutable
图片和媒体文件
- 长期缓存
- 文件名包含内容哈希
Cache-Control: max-age=31536000
API响应
- 根据数据更新频率设置
- 使用ETag进行验证
Cache-Control: max-age=60, must-revalidate
浏览器网络优化技术
预加载和预连接
浏览器支持多种资源预加载机制:
DNS预解析
html<link rel="dns-prefetch" href="https://example.com">
预连接
html<link rel="preconnect" href="https://example.com">
预加载
html<link rel="preload" href="/styles.css" as="style">
预获取
html<link rel="prefetch" href="/next-page.html">
HTTP/2和HTTP/3优化
现代浏览器利用新HTTP协议特性提高性能:
多路复用
- 在单个连接上并行请求多个资源
- 消除了HTTP/1.1的队头阻塞问题
头部压缩
- 减少重复头部字段传输
- 压缩头部数据
服务器推送
- 主动推送关联资源
- 减少客户端请求
QUIC协议 (HTTP/3)
- 建立连接更快
- 更好的拥塞控制
- 连接迁移支持
离线应用技术
浏览器提供多种技术实现离线Web应用:
Service Worker
- 拦截网络请求
- 实现自定义缓存策略
- 支持后台同步和推送通知
缓存API
javascript// 在Service Worker中使用Cache API cache.addAll([ '/', '/styles.css', '/script.js', '/images/logo.png' ]);
IndexedDB
- 存储大量结构化数据
- 支持索引和查询
- 适合离线应用数据存储
Web应用清单(Web App Manifest)
- 将网站添加到主屏幕
- 定义离线体验
浏览器缓存调试技巧
开发者工具
使用浏览器开发者工具检查和调试缓存:
Network面板
- 检查请求/响应头
- 查看缓存状态(Size列显示"(disk cache)"或"(memory cache)")
- 分析资源加载时间
禁用缓存功能
- Network面板中的"Disable cache"选项
- 仅在开发者工具打开时生效
清除缓存
- 使用Application面板中的Clear Storage功能
- 或使用Ctrl+F5/Cmd+Shift+R强制刷新
常见缓存问题及解决方案
缓存过期日期不正确
- 检查服务器时间配置
- 确保正确设置Cache-Control或Expires头
缓存未更新
- 使用内容哈希或版本号
- 更改资源URL(如添加查询参数)
中间代理缓存问题
- 使用Vary头控制缓存版本
- 明确设置private或public指令
不一致的缓存行为
- 确保所有资源使用一致的缓存策略
- 监控和测试不同浏览器的缓存行为
浏览器缓存
浏览器缓存机制
- 强缓存:浏览器在本地缓存中查找资源,如果找到且未过期,则直接使用缓存资源,否则继续请求服务器。
- 协商缓存:浏览器在本地缓存中查找资源,如果找到且未过期,则向服务器发送请求,服务器验证资源是否更新,如果更新则返回新资源,否则返回304状态码,浏览器使用本地缓存资源。
浏览器缓存策略
- Cache-Control:控制缓存的策略,如max-age、no-cache、no-store等。
- max-age:指定缓存的过期时间,单位为秒。
- no-cache:表示每次请求都需要验证缓存是否有效。
- no-store:表示不缓存资源。
- Expires:指定缓存的过期时间,格式为GMT时间。
- ETag:资源的唯一标识符,用于验证缓存是否有效。
- Last-Modified:资源的最后修改时间,用于验证缓存是否有效。
- If-None-Match:客户端发送的ETag值,服务器根据该值判断资源是否更新。
- If-Modified-Since:客户端发送的Last-Modified值,服务器根据该值判断资源是否更新。
- Cache-Control优先级高于Expires,ETag优先级高于Last-Modified。
- 浏览器会根据Cache-Control、Expires、ETag、Last-Modified等字段来判断资源是否有效,如果有效则使用缓存资源,否则向服务器请求新资源。