3. 数据录制原理:网络请求拦截、数据库变更捕获、日志文件解析

好,咱们进入正题。数据录制,说白了就是「把真实用户的操作过程给录下来」。你想想看,测试的时候最缺什么?缺真实数据。自己构造的数据总感觉差点意思,对吧?

我个人习惯把数据录制分成三个维度:网络层、数据层和文件层。这三个维度基本覆盖了绝大多数场景。今天咱们一个一个拆开讲。

3.1 网络请求拦截——最常用的录制方式

网络请求拦截,是我在项目中用得最多的方法。为什么?因为它简单、直接、覆盖广。前端发起的每一个请求,后端返回的每一个响应,都能被截获。

我记得有一次,要录制一个电商系统的下单流程。用户从浏览商品到支付成功,中间有十几个接口调用。如果用手工抓包,费时费力。但用拦截代理,一次录制,全部搞定。

3.1.1 代理拦截原理

说白了,就是在客户端和服务端之间插一个「中间人」。这个中间人能看到所有经过的流量。

核心思路: 启动一个本地代理服务器,修改客户端的代理设置,让所有HTTP/HTTPS流量都经过这个代理。

具体怎么做?我常用的工具是 mitmproxy 和 Charles。这里我以 mitmproxy 为例,给你看一段核心代码:

# 一个简单的mitmproxy拦截脚本
from mitmproxy import http

def request(flow: http.HTTPFlow) -> None:
    """拦截请求,记录URL和请求体"""
    # 我只关心API请求,过滤掉静态资源
    if not flow.request.pretty_url.startswith("https://api."):
        return
    
    # 记录请求信息
    print(f"[请求] {flow.request.method} {flow.request.pretty_url}")
    print(f"[请求体] {flow.request.text[:500]}")  # 只取前500字符
    
    # 这里可以写入文件或数据库
    save_request(flow.request)

def response(flow: http.HTTPFlow) -> None:
    """拦截响应,记录状态码和响应体"""
    if not flow.request.pretty_url.startswith("https://api."):
        return
    
    print(f"[响应] {flow.response.status_code}")
    print(f"[响应体] {flow.response.text[:1000]}")
    
    save_response(flow.response)
我的小技巧: 录制时一定要过滤掉静态资源(图片、CSS、JS)。不然数据量太大,而且没用。我一般只录制API接口,URL匹配规则用正则写好。

3.1.2 HTTPS 证书问题

嗯,这里要注意。HTTPS 的流量是加密的,代理要能解密,就得安装根证书。我曾经在这个坑里摔过一次——录制了半小时,发现全是加密乱码,白干了。

避坑指南: 在目标设备上安装 mitmproxy 的根证书。iOS 和 Android 的安装方式不同。iOS 需要去「设置-通用-关于本机-证书信任设置」里手动开启信任。Android 7.0 以上需要把证书安装到系统证书目录(需要root)。

3.1.3 录制数据的存储格式

录下来的数据怎么存?我建议用 JSON 格式,结构清晰,方便后续回放。每个请求-响应对作为一个独立记录。

{
  "id": "req_001",
  "timestamp": "2024-01-15T10:30:00.123Z",
  "method": "POST",
  "url": "https://api.example.com/order/create",
  "headers": {
    "Content-Type": "application/json",
    "Authorization": "Bearer xxx"
  },
  "request_body": {
    "product_id": "12345",
    "quantity": 2,
    "address_id": "67890"
  },
  "response_status": 200,
  "response_body": {
    "order_id": "ORD20240115001",
    "status": "success"
  },
  "duration_ms": 235
}

3.2 数据库变更捕获——后端视角的录制

网络请求拦截只能看到「输入输出」,但数据库里发生了什么变化?这个视角也很重要。特别是涉及事务、状态机变更的场景。

我习惯用 MySQL Binlog 或者 PostgreSQL 的 WAL(Write-Ahead Log) 来捕获数据变更。说白了,就是监听数据库的变更日志,把每一次 INSERT、UPDATE、DELETE 都记录下来。

3.2.1 Binlog 监听实战

以 MySQL 为例,用 canal 这个开源工具来监听 Binlog。部署起来很简单:

# 1. 开启 MySQL 的 Binlog(my.cnf 配置)
[mysqld]
log-bin=mysql-bin
binlog-format=ROW
server-id=1

# 2. 启动 canal(docker方式)
docker run -d --name canal \
  -e canal.instance.master.address=192.168.1.100:3306 \
  -e canal.instance.dbUsername=canal \
  -e canal.instance.dbPassword=canal123 \
  -e canal.instance.connectionCharset=UTF-8 \
  -e canal.instance.filter.regex=.*\\..* \
  registry.cn-hangzhou.aliyuncs.com/canal/canal-server:v1.1.6

监听到的数据长这样:

字段 说明 示例
table_name 变更的表名 orders
event_type 变更类型 INSERT / UPDATE / DELETE
before_data 变更前的数据(UPDATE/DELETE) {"status": "pending"}
after_data 变更后的数据(INSERT/UPDATE) {"status": "paid"}
timestamp 变更时间 2024-01-15 10:30:00
为什么需要数据库变更捕获? 网络请求拦截只能看到「谁调了什么接口」,但数据库变更捕获能看到「数据到底变成了什么样」。两者结合,才能完整还原一个操作。

3.3 日志文件解析——最后的兜底方案

有些场景,既不能拦截网络请求,也不能监听数据库。比如:老旧系统、第三方服务、没有源码的遗留系统。这时候怎么办?

我告诉你,看日志。几乎所有系统都会打印日志。日志文件就是最后的宝藏。

3.3.1 日志格式解析

日志格式千奇百怪。我见过最乱的日志,一行里混杂了时间、级别、线程名、类名、消息,还有 JSON 串。怎么解析?用正则表达式。

# 一个典型的 Java 应用日志
2024-01-15 10:30:00.123 [http-nio-8080-exec-1] INFO  c.e.OrderService - 创建订单成功: {"orderId":"ORD001","amount":299.00}

# 对应的正则解析
import re

log_pattern = re.compile(
    r'(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) '
    r'\[(?P<thread>[^\]]+)\] '
    r'(?P<level>\w+) '
    r'(?P<logger>[^\s]+) '
    r'- (?P<message>.*)'
)

def parse_log_line(line: str) -> dict:
    match = log_pattern.match(line)
    if match:
        return match.groupdict()
    return None

3.3.2 关键信息提取

日志里什么都有,但我们要提取的是 业务操作的关键信息。比如:

  • 操作类型:创建订单、支付、退款、取消
  • 操作对象:订单ID、用户ID、商品ID
  • 操作结果:成功、失败、异常信息
  • 时间戳:精确到毫秒
我的经验: 如果日志里包含 JSON 串,直接用 json.loads() 解析。如果日志是纯文本,那就得写正则。我建议先看 100 条日志,摸清规律再写解析器。别一上来就写,容易漏掉边界情况。

3.4 三种录制方式的对比

你可能会问:这三种方式,到底用哪个?我直接给你一张对比表:

录制方式 适用场景 优点 缺点
网络请求拦截 前后端分离、微服务架构 覆盖全面、实时性强 需要代理配置、HTTPS证书
数据库变更捕获 数据一致性要求高的场景 数据准确、可追溯 需要数据库权限、部署复杂
日志文件解析 老旧系统、第三方服务 无需侵入系统、通用性强 解析工作量大、日志格式不稳定

我个人习惯是:优先用网络请求拦截,因为它最直接。如果网络拦截搞不定,再考虑数据库变更捕获。日志解析是最后的兜底方案,但往往也是最费力的。

3.5 本章知识体系总览

说了这么多,我画了一张图帮你梳理一下。三种录制方式,各自有各自的定位,但最终目标都一样——把真实数据「录」下来,为后续的回放做准备。

数据录制原理 - 知识体系 数据录制 网络请求拦截 代理服务器(mitmproxy/Charles) HTTPS证书安装与信任 JSON格式存储请求/响应 数据库变更捕获 MySQL Binlog / PostgreSQL WAL Canal / Debezium 工具 INSERT/UPDATE/DELETE 记录 日志文件解析 正则表达式解析日志 提取操作类型/对象/结果 兜底方案,适用老旧系统 三种方式结合使用,覆盖所有录制场景 网络层 → 数据层 → 文件层,层层递进

好了,这一章的内容就到这里。三种录制方式,各有各的用武之地。你在实际项目中,可以根据系统的特点灵活选择。记住一个原则:能用网络拦截就用网络拦截,不行再考虑数据库,最后才是日志。这个顺序,是我踩过无数坑之后总结出来的。

可可精品资料库