Axios HTTP/2 实战指南:httpVersion 配置、http2Options 调优与 Node.js 会话池实现原理
Axios 自 v1.13.0 起在 Node.js 环境的 http 适配器中引入了实验性的 HTTP/2 支持。本文基于官方文档 HTTP/2 页面(英文原版),完整讲解 httpVersion 与 http2Options 两个核心配置项的用法、参数参考与限制条件,并结合 http 适配器源码 与 Http2Sessions 会话池 深入剖析其底层连接复用、空闲会话超时回收的实现机制,帮助你在 Node.js 服务中安全、高效地启用 HTTP/2 请求。
一、适用范围与启用前提
HTTP/2 支持是 Axios 1.13.0 版本加入 http 适配器的**实验性(experimental)**特性,当前仓库版本为 1.19.0,该功能持续可用但 API 仍可能在后续 minor 或 patch 版本中变动。关键限制如下:
- 仅限 Node.js 环境:HTTP/2 依赖 Node.js 内置
http2模块。从源码注释可以确认,Http2Sessions 是 Node-only 模块——浏览器/React Native 构建会通过package.json的browser字段把唯一的导入方 lib/adapters/http.js 替换为lib/helpers/null.js,因此这些环境永远不会触及 HTTP/2 路径; - 不支持代理:显式配置 Axios proxy 对象后发起
httpVersion: 2请求会直接抛出ERR_NOT_SUPPORT(见下文源码分析); - 不跟随重定向:HTTP/2 传输不会自动跟随
3xx响应,需要手动处理或对该类端点保持 HTTP/1.x。
二、基本用法:httpVersion 选项
使用请求配置中的 httpVersion 选项选择协议版本,设为 2 即启用 HTTP/2:
const { data, headers, status } = await axios.post(
"https://httpbin.org/post",
form,
{
httpVersion: 2,
},
);
httpVersion 是每请求生效的选项,默认值为 1。从 TypeScript 类型声明 可以看到其合法取值仅为 1 | 2:
httpVersion?: 1 | 2;
http2Options?: Record<string, any> & {
sessionTimeout?: number;
};
源码中对 httpVersion 有严格校验:先做数值转换,若结果是 NaN 抛出 Invalid protocol version 错误,若不是 1 或 2 则抛出 Unsupported protocol version(见 lib/adapters/http.js 第 592-602 行):
httpVersion = +httpVersion;
if (Number.isNaN(httpVersion)) {
throw TypeError(`Invalid protocol version: '${config.httpVersion}' is not a number`);
}
if (httpVersion !== 1 && httpVersion !== 2) {
throw TypeError(`Unsupported protocol version '${httpVersion}'`);
}
const isHttp2 = httpVersion === 2;
配置合并遵循 Axios 的标准实例级/请求级覆盖规则:实例级 httpVersion: 2 可以被单请求的 httpVersion: 1 覆盖,smoke 测试 对此有专门验证。
三、http2Options:透传原生选项与 sessionTimeout
内部 session.request() 调用的额外原生选项可通过配置对象 http2Options 传入。其中包含一个 Axios 自定义参数 sessionTimeout,控制空闲 HTTP/2 会话在被关闭前保持存活的时间(毫秒),默认值为 1000ms:
{
httpVersion: 2,
http2Options: {
rejectUnauthorized: false, // 接受自签名证书(仅限开发环境)
sessionTimeout: 5000, // 空闲会话保持 5 秒后关闭
},
}
除 sessionTimeout 外,Node.js 内置 http2 模块支持的所有原生 session.request() 选项(如 rejectUnauthorized、maxSessionHeaders、lookup 等)都可以放在 http2Options 中透传。实例级与请求级的 http2Options 会进行合并(请求级键覆盖实例级同名键),smoke 测试 验证了如下合并行为:
// 实例级: { rejectUnauthorized: false, sessionTimeout: 1000 }
// 请求级: { sessionTimeout: 5000, customFlag: true }
// 合并结果: { rejectUnauthorized: false, sessionTimeout: 5000, customFlag: true }
配置参考
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
httpVersion |
number |
1 |
使用的 HTTP 协议版本。设为 2 启用 HTTP/2。 |
http2Options.sessionTimeout |
number |
1000 |
空闲 HTTP/2 会话被关闭前的存活时间(毫秒)。 |
其余所有 Node.js http2 模块支持的原生 session.request() 选项均可通过 http2Options 传入。
四、完整示例:multipart 上传 + 双向进度监听
以下示例通过 HTTP/2 发送 multipart/form-data POST 请求,同时监听上传与下载进度:
const form = new FormData();
form.append("foo", "123");
const { data, headers, status } = await axios.post(
"https://httpbin.org/post",
form,
{
httpVersion: 2,
http2Options: {
// rejectUnauthorized: false,
// sessionTimeout: 1000
},
onUploadProgress(e) {
console.log("upload progress", e);
},
onDownloadProgress(e) {
console.log("download progress", e);
},
responseType: "arraybuffer",
},
);
该示例覆盖了 HTTP/2 传输的几个典型场景:FormData 流式上传、onUploadProgress/onDownloadProgress 事件回调、responseType: "arraybuffer" 二进制响应处理。
五、源码剖析:HTTP/2 请求是如何发出的
5.1 http2Transport:会话池与请求发起
lib/adapters/http.js 中定义了 HTTP/2 专属传输 http2Transport。当 isHttp2 为真时,适配器会选用该传输(而非 follow-redirects 的 httpFollow/httpsFollow):
const http2Transport = {
request(options, cb) {
const authority =
options.protocol +
'//' +
options.hostname +
':' +
(options.port || (options.protocol === 'https:' ? 443 : 80));
const { http2Options, headers } = options;
const session = http2Sessions.getSession(authority, http2Options);
const { HTTP2_HEADER_SCHEME, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS } =
http2.constants;
const http2Headers = {
[HTTP2_HEADER_SCHEME]: options.protocol.replace(':', ''),
[HTTP2_HEADER_METHOD]: options.method,
[HTTP2_HEADER_PATH]: options.path,
};
utils.forEach(headers, (header, name) => {
name.charAt(0) !== ':' && (http2Headers[name] = header);
});
const req = session.request(http2Headers);
req.once('response', (responseHeaders) => {
const response = req; //duplex
responseHeaders = Object.assign({}, responseHeaders);
const status = responseHeaders[HTTP2_HEADER_STATUS];
delete responseHeaders[HTTP2_HEADER_STATUS];
response.headers = responseHeaders;
response.statusCode = +status;
cb(response);
});
return req;
},
};
从源码结构看,这里有几个值得注意的细节:
- 按 authority(协议 + 主机 + 端口)复用会话:
authority形如https://example.com:443,同一 authority 且http2Options完全相同的请求会复用同一个http2.connect()会话; - 伪头处理:HTTP/2 要求
:scheme、:method、:path伪头,适配器自动从options构造;用户传入的以:开头的头会被显式跳过,避免污染伪头; - 状态码提取:HTTP/2 响应头中的
:status伪头被取出并转换为数字statusCode,其余头部原样透传为response.headers。
这正是文档中"HTTP/2 不跟随重定向"警告的根源——HTTP/1 路径走 follow-redirects 传输可以自动跟随 3xx,而 http2Transport 收到响应头后立即回调,3xx 状态码会原样暴露给调用方。
5.2 Http2Sessions:连接池与空闲超时回收
lib/helpers/Http2Sessions.js 是会话池的核心实现。其 getSession(authority, options) 方法(第 16-42 行):
- 将
sessionTimeout: 1000作为默认值注入options; - 遍历该 authority 已缓存的会话,仅当会话未被销毁、未关闭,且其创建选项与本次
options通过util.isDeepStrictEqual深度相等时才复用——这意味着如果你在下一次请求中改变了http2Options(哪怕只改一个字段),会新建一个独立会话,而不是复用旧的; - 否则调用
http2.connect(authority, options)建立新会话并缓存。
空闲超时逻辑(第 80-106 行)通过包装 session.request 实现:
const { sessionTimeout } = options;
if (sessionTimeout != null) {
let streamsCount = 0;
session.request = function () {
const stream = originalRequestFn.apply(this, arguments);
streamsCount++;
if (timer) {
clearTimeout(timer);
timer = null;
}
stream.once('close', () => {
if (!--streamsCount) {
timer = setTimeout(() => {
timer = null;
removeSession();
}, sessionTimeout);
}
});
return stream;
};
}
机制可以概括为:每次发起请求时取消待触发的关闭定时器并累加活跃流计数;当最后一个流 close 后启动 sessionTimeout 毫秒的定时器,到期仍未有新请求就关闭并从池中移除该会话。若 sessionTimeout 设为 null/undefined(显式传入),则不做空闲回收,会话常驻至 close/error 事件触发 removeSession(第 108-109 行)。removeSession 同时处理了定时器清理、会话从 authority 数组中摘除和 session.close() 调用,且做了幂等保护。
5.3 代理、DNS lookup 与特殊约束
- 代理互斥:lib/adapters/http.js 第 1012-1023 行 中,若请求应用了 proxy 且
isHttp2为真,直接以HTTP/2 requests with a proxy are not supported拒绝(ERR_NOT_SUPPORT)。源码注释说明:HTTP/2 传输独立于 HTTP/1 agent 建立连接,无法应用 axios 解析出的代理或 agent 的环境代理,因此显式 proxy 配置被显式拒绝。proxy: false则继续强制直连; - 自定义 DNS lookup 透传:第 925-927 行 中,若配置了
lookup,HTTP/2 请求会将其注入http2Options,使自定义解析函数同样作用于 HTTP/2 连接,且因 options 深度比较一致仍能安全复用池化会话:
if (isHttp2 && lookup) {
http2Options = Object.assign(Object.create(null), http2Options, { lookup });
}
六、实践建议
- 实验性定位:HTTP/2 API 仍标注 experimental,升级 Axios 版本时建议关注变更日志中 "HTTP/2" 相关条目;
- 重定向敏感端点:依赖服务端
3xx跳转的业务保持httpVersion: 1(或不设置),或自行捕获 3xx 并手动发起新请求; sessionTimeout调优:默认 1000ms 偏激进,请求间隔超过 1 秒就会重建连接。对高频调用同一 authority 的场景,可适当调大(如 5000-10000ms)以获得连接复用的收益;- 保持
http2Options稳定:由于会话复用要求选项深度相等,频繁变化的http2Options会导致会话池碎片化、连接数上升; - 开发环境自签名证书:
rejectUnauthorized: false仅在开发环境使用,生产环境勿开。
七、测试验证
tests/smoke/cjs/tests/http2.smoke.test.cjs(及 esm 版本)对配置传递做了三类断言,可作为行为契约参考:
- 实例级
httpVersion与http2Options完整保留在请求 config 中; - 请求级
http2Options与实例级正确合并(同名键请求级胜出); - 请求级
httpVersion可覆盖实例级设置。
配合 Http2Sessions 单元测试 与 http 适配器测试,你可以在本地运行 smoke 测试套件验证 HTTP/2 相关行为是否符合预期。
小结
Axios 的 HTTP/2 支持通过 httpVersion: 2 一行配置即可在 Node.js 中启用,http2Options 则提供了原生 http2 选项透传与 sessionTimeout 空闲回收控制。理解其底层的 authority 会话池、选项深度相等复用规则和 1000ms 默认空闲超时,能让你在连接复用、资源释放与重定向/代理限制之间做出正确的工程权衡。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00