HTTP 单连接与多连接及预检请求详解

一、HTTP 单连接与多连接

1. 单连接模式 (HTTP/1.1 默认)

特点:

  • 同一域名下所有请求共享一个TCP连接

  • 请求必须按顺序发送和接收(队头阻塞问题)

  • 通过 Connection: keep-alive 保持长连接

示例:

复制代码
客户端: GET /resource1
服务器: 响应1
客户端: GET /resource2 (必须等待响应1完成)
服务器: 响应2

2. 多连接模式

HTTP/1.1 解决方案:

  • 浏览器为同一域名打开多个TCP连接(通常6-8个)

  • 并行发送请求,缓解队头阻塞

HTTP/2 多路复用:

  • 单一TCP连接上并行交错传输多个请求/响应

  • 二进制分帧层解决队头阻塞

  • 请求/响应可以交错,不受顺序限制

对比表:

特性 HTTP/1.1 单连接 HTTP/1.1 多连接 HTTP/2
连接数 1 6-8 1
队头阻塞 严重 部分缓解 完全解决
资源开销
服务器压力

二、HTTP 预检请求 (Preflight Request)

1. 什么是预检请求

预检请求是浏览器在发送某些跨域请求前,先使用 OPTIONS 方法发起一个预检请求,以确定实际请求是否安全的机制。

2. 触发预检的条件

当请求满足以下任一条件时,浏览器会发送预检请求:

  1. 使用特定HTTP方法

    • 非简单方法:PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH
  2. 设置了非简单头部字段

    • 非简单头部:除以下之外的头部

      • Accept

      • Accept-Language

      • Content-Language

      • Content-Type(仅限特定值)

      • DPR

      • Downlink

      • Save-Data

      • Viewport-Width

      • Width

  3. Content-Type 非简单值

    • 非以下类型:text/plainmultipart/form-dataapplication/x-www-form-urlencoded

3. 预检请求示例

客户端发送OPTIONS预检:

复制代码
OPTIONS /resource HTTP/1.1
Host: api.example.com
Origin: https://yourdomain.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: X-Custom-Header

服务器响应:

复制代码
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://yourdomain.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 86400

4. 预检请求工作流程

  1. 浏览器检测到需要预检的跨域请求

  2. 先发送OPTIONS请求进行预检

  3. 服务器响应是否允许实际请求

  4. 如果允许,浏览器发送实际请求

  5. 服务器响应实际请求

5. 避免预检的方法

  1. 使用简单请求

    • GET/HEAD/POST方法

    • 仅使用简单头部

    • Content-Type为简单类型

  2. 利用缓存

    • 设置Access-Control-Max-Age头部

      Access-Control-Max-Age: 86400 // 缓存1天

  3. 后端配置CORS

    • 正确配置Access-Control-Allow-*头部

    • 处理OPTIONS请求

三、实际开发中的场景示例

1. 触发预检的请求

javascript 复制代码
// 自定义头部触发预检
fetch('https://api.example.com/data', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'X-Auth-Token': 'abc123'  // 自定义头部
  },
  body: JSON.stringify({ key: 'value' })
});

2. 不触发预检的简单请求

javascript 复制代码
// 简单GET请求
fetch('https://api.example.com/data');

// 简单POST表单请求
fetch('https://api.example.com/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  body: 'key1=value1&key2=value2'
});

3. Node.js 服务端CORS配置示例

javascript 复制代码
// Express 中间件
app.use((req, res, next) => {
  // 允许的源
  res.setHeader('Access-Control-Allow-Origin', 'https://yourdomain.com');
  
  // 允许的方法
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE');
  
  // 允许的头部
  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Authorization');
  
  // 预检请求缓存时间
  res.setHeader('Access-Control-Max-Age', '86400');
  
  // 处理预检请求
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  
  next();
});

理解这些机制有助于优化Web应用性能(通过合理使用连接)和正确处理跨域请求(通过理解预检机制)。