客户让我给她写个爬虫-DNS 解析到响应处理

13 阅读6分钟

客户让我给她写个爬虫

深入理解 HTTP 请求过程:从 DNS 解析到响应处理

前言

在开发中,我们经常使用各种 HTTP 客户端工具(如 HttpClient)发送请求,但你是否真正了解一个 HTTP 请求从发出到接收响应的完整过程?本文将深入讲解 HTTP 请求的每个环节,帮助你更好地理解网络通信原理。

1. DNS 解析:从域名到 IP 地址

当我们在代码中访问restapi.amap.com时,首先需要将域名转换为 IP 地址,这个过程就是 DNS 解析。

1.1 本地 DNS 缓存查询

系统会按照以下顺序查找 DNS 缓存:

  1. 操作系统 DNS 缓存

    • Windows 系统:ipconfig /displaydns查看
    • Linux 系统:cat /etc/resolv.conf查看
  2. 浏览器 DNS 缓存

    • Chrome:chrome://net-internals/#dns查看
    • 缓存时间由 TTL(Time To Live)控制
  3. Java DNS 缓存

    • JVM 维护的 DNS 缓存
    • 可通过networkaddress.cache.ttl属性配置

1.2 本地 hosts 文件查询

如果本地缓存没有,系统会查询 hosts 文件:

# Windows路径
C:\Windows\System32\drivers\etc\hosts

# Linux路径
/etc/hosts

1.3 DNS 服务器查询

如果 hosts 文件也没有记录,系统会向 DNS 服务器发起查询:

  1. 本地 DNS 服务器查询

    • 向配置的 DNS 服务器发送查询请求
    • 通常由 ISP 提供
  2. 递归查询过程

    • 本地 DNS 服务器向根 DNS 服务器查询
    • 根 DNS 服务器返回.com 域名的 DNS 服务器
    • 查询.com 域名的 DNS 服务器
    • 获取 amap.com 的 DNS 服务器
    • 最终获取 restapi.amap.com 的 IP 地址
  3. DNS 缓存更新

    • 将解析结果缓存到本地
    • 设置 TTL(生存时间)
    • 过期后需要重新查询

2. TCP 连接:可靠传输的基础

获取 IP 地址后,需要建立 TCP 连接,这是通过著名的"三次握手"完成的。

2.1 三次握手过程

  1. 第一次握手

    • 客户端发送 SYN 包
    • 设置初始序列号(ISN)
    • 设置 TCP 标志位(SYN=1)
    • 设置窗口大小
  2. 第二次握手

    • 服务器返回 SYN-ACK 包
    • 确认客户端序列号(ACK=ISN+1)
    • 设置服务器序列号
    • 设置 TCP 标志位(SYN=1, ACK=1)
  3. 第三次握手

    • 客户端发送 ACK 包
    • 确认服务器序列号
    • 设置 TCP 标志位(ACK=1)
    • 连接建立完成

2.2 TCP 参数设置

在握手过程中,双方会协商以下参数:

  1. MSS(最大报文段大小)

    • 决定每个 TCP 包的最大数据量
    • 通常为 1460 字节(MTU 1500 - 40 字节 TCP/IP 头)
  2. 窗口缩放因子

    • 用于支持更大的窗口大小
    • 提高传输效率
  3. 选择性确认(SACK)

    • 允许接收方确认不连续的数据块
    • 提高传输可靠性

3. TLS 握手:安全通信的保障

由于使用 HTTPS,在 TCP 连接建立后需要进行 TLS 握手。

3.1 客户端 Hello

客户端发送以下信息:

  1. TLS 版本

    • 支持的 TLS 版本列表
    • 按优先级排序
  2. 加密套件

    • 支持的加密算法组合
    • 包括密钥交换算法
    • 对称加密算法
    • 消息认证算法
  3. 随机数

    • 客户端生成的随机数
    • 用于生成会话密钥

3.2 服务器 Hello

服务器响应:

  1. 版本选择

    • 选择双方都支持的最高 TLS 版本
  2. 加密套件选择

    • 选择双方都支持的最安全的加密套件
  3. 证书发送

    • 发送服务器证书链
    • 包含中间证书
    • 包含根证书

3.3 密钥交换

  1. 预主密钥生成

    • 客户端生成随机预主密钥
    • 使用服务器公钥加密
  2. 会话密钥生成

    • 使用预主密钥
    • 使用客户端随机数
    • 使用服务器随机数
    • 生成主密钥
    • 生成会话密钥

4. HTTP 请求发送:应用层通信

4.1 请求头构建

  1. 基本头信息

    Host: restapi.amap.com
    User-Agent: Java/1.8.0_xxx
    Accept: */*
    Connection: keep-alive
    
  2. 自定义头信息

    Content-Type: application/json
    Authorization: Bearer token
    

4.2 请求参数处理

  1. URL 编码

    • 对特殊字符进行编码
    • 使用 UTF-8 字符集
  2. 查询字符串构建

    • 参数排序
    • 参数拼接
    • 添加分隔符

4.3 数据发送

  1. 分片处理

    • 大数据分片
    • 按 MSS 大小分片
  2. 流量控制

    • 使用滑动窗口
    • 控制发送速率
  3. 拥塞控制

    • 慢启动
    • 拥塞避免
    • 快速恢复

5. 服务器处理:业务逻辑执行

5.1 请求接收

  1. 请求解析

    • 解析 HTTP 请求头
    • 解析请求参数
    • 验证 API 密钥
  2. 参数验证

    • 检查必填参数
    • 验证参数格式
    • 验证参数范围

5.2 业务处理

  1. 数据库查询

    • 构建查询语句
    • 执行查询
    • 获取结果集
  2. 业务逻辑

    • 数据处理
    • 业务规则验证
    • 结果计算

5.3 响应构建

  1. 响应头设置

    HTTP/1.1 200 OK
    Content-Type: application/json
    Content-Length: xxx
    
  2. 响应体构建

    • 生成 JSON 数据
    • 设置编码
    • 计算长度

6. 响应返回:数据回传

6.1 数据加密

  1. 会话密钥加密

    • 使用协商的会话密钥
    • 加密响应数据
  2. 消息认证

    • 生成 MAC
    • 验证数据完整性

6.2 数据发送

  1. 分片处理

    • 按 MSS 大小分片
    • 添加序列号
  2. 流量控制

    • 使用滑动窗口
    • 控制发送速率

6.3 客户端接收

  1. 数据解密

    • 使用会话密钥
    • 解密数据包
  2. 数据重组

    • 按序列号排序
    • 合并数据片

7. 连接处理:资源管理

7.1 短连接处理

  1. 连接关闭

    • 发送 FIN 包
    • 等待 ACK
    • 释放资源
  2. 资源清理

    • 关闭 socket
    • 释放内存
    • 清理缓存

7.2 长连接处理

  1. 连接保持

    • 设置 keep-alive
    • 定期发送心跳
    • 检测连接状态
  2. 连接复用

    • 维护连接池
    • 复用已有连接
    • 避免重复握手

8. 错误处理:异常情况处理

8.1 超时处理

  1. 连接超时

    • 设置连接超时时间
    • 超时后重试
    • 记录错误日志
  2. 读取超时

    • 设置读取超时时间
    • 中断读取操作
    • 释放资源

8.2 重试机制

  1. 重试策略

    • 指数退避
    • 最大重试次数
    • 重试条件判断
  2. 错误恢复

    • 清理失败连接
    • 重新建立连接
    • 继续处理请求

总结

一个 HTTP 请求从发出到接收响应的过程涉及多个网络层次,每个环节都可能影响最终的性能和可靠性。理解这些过程有助于我们:

  1. 更好地处理网络问题
  2. 优化应用性能
  3. 提高系统可靠性
  4. 进行问题诊断

希望本文能帮助你更深入地理解 HTTP 请求过程。如果你有任何问题,欢迎在评论区讨论。