Chromium 一次网络请求全流程
2026-03-11
我们每天都在使用浏览器。
你的每一次网络请求,实际都跨越了千山万水,需要浏览器、操作系统、网络基础设施各方力量的鼎立配合。
在用户眼中,看上去无非只是点击几次鼠标,就能拿到想要的结果。
但这个过程,远比看起来复杂。
今天这篇文章,不同于常规的面试回答,而是从 Chromium 底层视角出发,完整描述发生在一次请求背后的故事,琐碎,但异常精彩。
如果拿着这个答案去和面试官 Battle,或许他会惊叹于你对这个问题的理解深度。
我们即将看到,在充满不确定性的世界里,Chromium 如何将访问网络这件事,变得确定化。
整个流程,分四部分:准备工作;发起请求;提交渲染;显示。
比将大象装进冰箱,多了一步。
1. 准备工作
在发起网络请求前,预先要做两件事:
- 参数填充
- 注册 Throttle
NavigationControllerImpl::LoadURLWithParams() 会根据传入参数,创建一个贯穿整个请求过程的 NavigationRequest。
同时,NavigationThrottleRunner 注册一系列类似 Hook 的 Throttles。
官方文档解释是:
NavigationThrottles allow observing, deferring, blocking, and canceling a given navigation. (NavigationThrottles 允许观察、延迟、阻止和取消给定的导航。)
可作用于导航过程中某些特定阶段:
WillStartRequest— 网络请求发起前WillRedirectRequest— 重定向WillFailRequest— 请求失败WillProcessResponse— 请求结果到达WillCommitWithoutUrlLoader— 不需要 URL 加载器的导航 (比如,about:blank)
整个过程发生在 Browser Process(浏览器进程)。
2. 发起请求
网络相关的功能,被放置在一个独立的 Network Process(网络进程)里。
这是一个沙盒进程(Sandbox Process),权限低(Low-Privilege)。
两点好处:
- 服务解耦——逻辑清晰,便于维护
- 安全——即便攻击者攻破网络进程,因为权限低,做不了什么坏事
详细架构如下:
┌────────────────────────────────────────────────────────────────────────┐│ Browser Process ││ ││ NavigationURLLoaderImpl, RenderFrameHost, etc. ││ │ ││ │ Mojo IPC ││ ▼ │├────────────────────────────────────────────────────────────────────────┤│ Network Service Process ││ ││ ┌──────────────┐ ││ │NetworkService│──── the singleton service ││ └──────┬───────┘ ││ │ CreateNetworkContext() ││ ▼ ││ ┌──────────────┐ ││ │NetworkContext │──── wraps a net::URLRequestContext ││ │ │ (cookies, cache, proxy, DNS config per profile) ││ └──────┬───────┘ ││ │ CreateURLLoaderFactory() ││ ▼ ││ ┌────────────────┐ ││ │URLLoaderFactory│──── one per frame/context ││ └──────┬─────────┘ ││ │ CreateLoaderAndStart() ││ ▼ ││ ┌──────────┐ ││ │ URLLoader│──── drives a single net::URLRequest ││ └──────┬───┘ ││ │ ││ ▼ ││ ┌─────────────────────────────────────┐ ││ │ net::URLRequest → URLRequestHttpJob │ ││ │ → HttpCache → HttpNetworkTransaction ││ │ → DNS → TCP → TLS → HTTP/2/3 ││ └─────────────────────────────────────┘ │└────────────────────────────────────────────────────────────────────────┘通过 Mojo 这种 IPC 机制,Browser Process 将后续工作移交给 Network Process。
真正的请求从 URLRequest 开始,在 URLRequest::StartJob 中开启一段网络之旅:填充请求元数据(RequestInfo),抓取 Cookies,借助 NetworkDelegate 创建一个 HttpTransaction。
下面就是经典的流水线操作:
- 检查硬盘缓存
- 缓存不命中或失效,创建一个套接字:
- 解析 Proxy ——
ProxyResolutionService - 解析 DNS ——
HostResolver - 建立 TCP 连接 ——
TCPClientSocket - 对于 HTTPS 还需要 TLS 握手 ——
SSLClientSocket,主要包括:- 证书验证
- 密钥交换
- ALPN 协商 (HTTP 1.1,HTTP 2/3)
- 解析 Proxy ——
发送 HTTP 请求,等待响应,然后交给 Browser Process 处理。
先收到 Response headers,包括:
- 状态码
- 内容类型
- 内容编码
- 安全头等(CSP,HSTS 等)
接着需要处理:
- MIME 类型嗅探 ——
MimeSniffingResourceHandler - 跨域检查(Cross-Origin Checks) —— 跨域的读取会被禁止
- 执行内容安全策略(CSP)
- 下载检测 —— 根据 MIME 类型,如果是不可渲染结果,比如
application/zip,返回内容被交给下载组件 - 进程选择 —— 响应结果会交给合适的 Render Process 处理并展示,遵循”站点隔离(Site Isolation)“原则
3. 提交渲染
继续借助 IPC,Browser Process 将剩余工作传递给 Render Process:
- 响应头(Response headers)
- 一个
mojo::DataPipe用于传输响应体(Response body) - 安全,导航等相关参数
紧接着,就是 HTML 解析:
- 预加载扫描(Preload scanner),提前发现子资源,例如:<script>, <link>, <img>,并发抓取(Parallel fetch);像那些阻碍渲染的资源,<script> 或 <line rel=“stylesheet”>,会暂停解析,直到加载完成
- 分词(Tokenizer)
- 构建树(Tree builder)
剩下的,就是 CSS 解析,样式计算,布局(Layout),绘制(Paint),组合(Compositing)。
之前 文章🔗 文章过。
4. 显示
Render Process 将 CompositorFrame 交给 GPU Process。
GPU Process 将所有的帧(浏览器 UI 界面、每个渲染器帧等)聚合,SkiaRenderer 绘制最终的四边形(Quad)。
为保证结果的完整呈现,使用交换缓冲区(Swap buffers),完成的帧被呈现给操作系统窗口系统(Windows 系统使用 DXGI 交换链,其他平台使用 EGL),经过 VSync(Vertical Synchronization,垂直同步),显示器展示。
5. 总结
需要承认,这篇文章写得很困难。
一方面源于,Chromium 本身就是一个超级复杂的工程,组件和代码众多,年代久远,文档也不完备,网络上可参考的有价值的文章并不多,另一方面为保证准确,需要精确到代码行数的调试。
理论,结合实践,才能不断地逼近事情真相。
好在现在有 AI,极大简化了代码检索和组织的难度,但开发者的判断和确认依然必要:同样一个问题,我去问 GPT 和 Gemini,结果有时还会打架。这就需要你去 Codebase 中核对。
相信你也看出来了,本篇文章,其实只是以 “Chromium 一次网络请求全流程” 为引子,测试 AI 对于一个复杂问题理解和描述的能力。
已经做得足够好了。
我想,在后开发时代,已经有了 AI 这样强大的工具,普通开发者需要思考如何适应新的角色。
根据这次实验,我认为,提出正确问题(Right Question)、品味(Taste)、判断力(Judgement),依然重要。
AI 很强,但并非全能,至少目前如此。
(完)
参考
- 本文作者:Plantree
- 本文链接:https://plantree.me/blog/2026/chromium-load-url/
- 版权声明:所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
最后更新于: 2026-03-11T08:51:04+08:00