【前端方案】设计一个全站请求耗时统计工具

参考方案:设计一个全站请求耗时统计工具


一、目标与价值

✅ 目标

构建一个覆盖全站请求的性能监控系统,用于收集、分析和展示每个 HTTP 请求的耗时情况,包括:

  • 用户端视角(前端)
  • 网络传输耗时(CDN/中间层)
  • 后端服务处理时间(接口层)
  • 数据库/第三方服务响应时间

📈 价值

  • 提升用户体验:识别慢请求,优化页面加载速度
  • 故障排查:快速定位性能瓶颈或异常接口
  • 性能趋势分析:监控不同时间段的请求性能变化
  • 支持 A/B 测试:对比不同版本接口性能差异
  • 保障 SLA:确保接口响应时间符合服务等级协议

二、整体架构设计

[用户浏览器] → [CDN / Nginx] → [网关 / 接入层] → [后端服务] → [数据库 / 第三方服务]
     ↓                ↓                 ↓                  ↓
[前端埋点]     [日志采集 & 中间层追踪]    [链路追踪]      [DB Profiling]
     ↓                ↓                 ↓                  ↓
       └──────→ [统一上报中心] ←────────┘
                             ↓
                   [数据存储(ES / MySQL)]
                             ↓
                     [可视化看板 / 报警系统]

三、核心功能模块详解

1. 前端埋点(Client-Side Monitoring)

功能点:
  • 页面首次加载时间(FP、FCP、LCP)
  • 每个 API 请求的发起时间、响应时间
  • 资源加载耗时(JS/CSS/图片)
  • 网络环境(是否使用 CDN、运营商信息)
实现方式:
  • 使用 Performance API(如 performance.getEntries()
  • 封装 Axios 插件记录接口耗时
  • 上报格式示例:
{
  "url": "/api/user/list",
  "method": "GET",
  "startTime": "2025-05-01T12:00:00Z",
  "endTime": "2025-05-01T12:00:00.350Z",
  "duration": 350,
  "userId": "user_123",
  "env": "production",
  "device": "Chrome 117, Windows 10"
}

定义请求耗时数据的接口:

// 定义请求耗时数据的接口
interface RequestPerformanceData {
  url: string;         // 请求的 URL
  method: string;      // 请求方法(GET、POST 等)
  startTime: string;   // 请求开始时间(ISO 8601 格式)
  endTime: string;     // 请求结束时间(ISO 8601 格式)
  duration: number;    // 请求耗时(毫秒)
  userId: string;      // 用户 ID
  env: string;         // 环境(如 production、development)
  device: string;      // 设备信息(浏览器和操作系统)
}

// 示例数据
const performanceData: RequestPerformanceData = {
  url: "/api/user/list",
  method: "GET",
  startTime: "2025-05-01T12:00:00Z",
  endTime: "2025-05-01T12:00:00.350Z",
  duration: 350,
  userId: "user_123",
  env: "production",
  device: "Chrome 117, Windows 10"
};

// 解析并注释请求耗时数据
function parseAndLogPerformanceData(data: RequestPerformanceData): void {
  // 解析请求 URL
  console.log(`1. 请求 URL: ${data.url}`);

  // 解析请求方法
  console.log(`2. 请求方法: ${data.method}`);

  // 解析请求开始时间
  console.log(`3. 请求开始时间: ${data.startTime}`);

  // 解析请求结束时间
  console.log(`4. 请求结束时间: ${data.endTime}`);

  // 计算并解析请求耗时
  console.log(`5. 请求耗时: ${data.duration} 毫秒`);

  // 解析用户 ID
  console.log(`6. 用户 ID: ${data.userId}`);

  // 解析环境
  console.log(`7. 环境: ${data.env}`);

  // 解析设备信息
  console.log(`8. 设备信息: ${data.device}`);
}

// 调用函数并传入示例数据
parseAndLogPerformanceData(performanceData);

代码说明:

  1. 接口定义

    • RequestPerformanceData 接口定义了请求耗时数据的结构,包括 URL、方法、时间戳、耗时、用户 ID、环境和设备信息。
  2. 示例数据

    • performanceData 是一个符合 RequestPerformanceData 接口的对象,包含示例数据。
  3. 解析函数

    • parseAndLogPerformanceData 函数接收 RequestPerformanceData 类型的参数,逐行解析并打印数据。
    • 每行代码都包含详细的注释,说明解析的字段及其含义。
  4. 输出

    • 调用 parseAndLogPerformanceData(performanceData) 后,控制台会输出以下内容:
      1. 请求 URL: /api/user/list
      2. 请求方法: GET
      3. 请求开始时间: 2025-05-01T12:00:00Z
      4. 请求结束时间: 2025-05-01T12:00:00.350Z
      5. 请求耗时: 350 毫秒
      6. 用户 ID: user_123
      7. 环境: production
      8. 设备信息: Chrome 117, Windows 10
      

扩展建议:

  • 如果需要进一步处理这些数据(如存储到数据库或发送到监控系统),可以在 parseAndLogPerformanceData 函数中添加相关逻辑。
  • 对于时间戳(startTimeendTime),可以使用 Date 对象进行更复杂的操作(如计算时间差、格式化等)。
  • 如果数据量较大,可以考虑使用日志库(如 winstonpino)替代 console.log
埋点时机:
  • 页面加载完成
  • 每次接口调用结束(成功/失败)
  • 页面关闭前(beforeunload)

2. 网络层埋点(CDN/Nginx/反向代理)

功能点:
  • 客户端到服务器之间的网络延迟
  • CDN 缓存命中率
  • SSL 握手耗时
  • 请求排队等待时间(如限流队列)
实现方式:
  • 配置 Nginx 日志输出字段,例如:
log_format timing '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" $request_time $upstream_response_time';
  • 字段说明:
    • $request_time: 从客户端建立连接到请求完成的总时间
    • $upstream_response_time: 后端服务响应时间
示例日志:
192.168.1.1 - - [01/May/2024:12:00:00 +0800] "GET /api/user/list HTTP/1.1" 200 1234 "-" "Mozilla/5.0" 0.350 0.280

3. 后端服务埋点(Server-Side Tracing)

功能点:
  • 接口处理耗时(排除网络因素)
  • 方法执行时间(如 Controller 层、Service 层、DAO 层)
  • 异常堆栈记录
  • 请求上下文传递(Trace ID、User ID)
实现方式:
  • 使用 OpenTelemetry / Zipkin / Jaeger 等分布式追踪框架
  • 使用拦截器封装耗时统计逻辑(Spring AOP / Node.js Middleware)
示例结构(Node.js Express):
// 定义一个 Express 中间件函数,用于记录请求的耗时和相关信息
app.use((req, res, next) => {
  // 记录请求开始时间(使用 Date.now() 获取当前时间戳)
  const start = Date.now();

  // 生成一个唯一的请求标识符(traceId),用于跟踪一次完整的请求
  // 假设 uuidv4() 是一个生成 UUID v4 的函数(需要提前导入或定义)
  const traceId = uuidv4();

  // 将 traceId 附加到请求对象上,以便后续中间件或路由处理程序使用
  req.traceId = traceId;

  // 监听响应对象的 'finish' 事件,该事件在响应结束时触发
  res.on('finish', () => {
    // 计算请求耗时:响应结束时间 - 请求开始时间
    const duration = Date.now() - start;

    // 调用日志记录函数,记录请求的详细信息
    logToMonitor({
      traceId,                     // 唯一标识符,用于跟踪请求
      url: req.originalUrl,        // 请求的原始 URL(包含查询参数)
      method: req.method,          // 请求方法(GET、POST、PUT、DELETE 等)
      status: res.statusCode,      // 响应状态码(如 200、404、500 等)
      duration,                    // 请求耗时(毫秒)
      userId: req.user?.id || 'anonymous', // 用户 ID(如果已认证),否则为 'anonymous'
    });
  });

  // 调用 next() 将控制权传递给下一个中间件或路由处理程序
  next();
});

代码说明:

  1. 中间件定义

    • app.use((req, res, next) => { ... }) 是 Express 的中间件定义方式,用于处理所有传入的请求。
  2. 请求开始时间

    • const start = Date.now() 记录请求的开始时间,用于后续计算请求耗时。
  3. 唯一请求标识符

    • const traceId = uuidv4() 生成一个唯一的 UUID v4 标识符,用于跟踪一次完整的请求生命周期。
  4. 附加 traceId 到请求对象

    • req.traceId = traceId 将生成的 traceId 附加到请求对象 req 上,后续中间件或路由可以通过 req.traceId 访问它。
  5. 监听响应结束事件

    • res.on('finish', () => { ... }) 监听响应对象的 finish 事件,该事件在响应发送给客户端后触发。
  6. 计算请求耗时

    • const duration = Date.now() - start 计算从请求开始到响应结束的总耗时(毫秒)。
  7. 记录请求信息

    • logToMonitor({ ... }) 调用日志记录函数,记录请求的详细信息,包括:
      • traceId:唯一标识符。
      • url:请求的原始 URL。
      • method:请求方法。
      • status:响应状态码。
      • duration:请求耗时。
      • userId:用户 ID(如果已认证),否则为 'anonymous'
  8. 传递控制权

    • next() 调用 next() 将控制权传递给下一个中间件或路由处理程序。

注意事项:

  • 需要确保 uuidv4() 函数已正确定义或导入(例如使用 uuid 库的 v4 方法)。
  • logToMonitor 是一个自定义函数,需要根据实际需求实现(如写入数据库、发送到监控系统等)。
  • 如果用户认证信息存储在 req.user 中,需要确保认证中间件已正确设置 req.user

4. 数据库层埋点(Database Profiling)

功能点:
  • 单条 SQL 执行时间
  • 是否命中索引
  • 是否进行文件排序、临时表等操作
  • 连接池状态(是否有等待)
实现方式:
  • 开启慢查询日志(MySQL 的 slow query log
  • 使用 ORM 工具插件记录 SQL 耗时(如 TypeORM、Sequelize、MyBatis Interceptor)
  • 使用 APM 工具(如 SkyWalking、Pinpoint)自动采集
示例日志:
# Time: 2024-05-01T12:00:00.100Z
# Query_time: 0.250
SELECT * FROM users WHERE name LIKE '%john%';

5. 第三方服务埋点(External Service Monitoring)

功能点:
  • 外部 API 调用耗时(如支付、短信、地图服务)
  • 服务可用性(是否超时、失败次数)
  • 并发请求数、失败重试机制
实现方式:
  • 使用 SDK 包装外部调用(如 axios.interceptors)
  • 记录每次调用的开始时间和结束时间
  • 添加唯一 Trace ID 以便关联整条链路
示例代码(axios):

使用 Axios 拦截器记录第三方服务请求耗时和状态的实现:

// 创建一个自定义的 Axios 实例,用于发送 HTTP 请求
const apiClient = axios.create();

// 添加请求拦截器
apiClient.interceptors.request.use(config => {
  // 在请求配置中添加 metadata 对象,记录请求的开始时间
  config.metadata = { startTime: new Date() };

  // 返回修改后的请求配置
  return config;
});

// 添加响应拦截器
apiClient.interceptors.response.use(
  // 成功响应的处理函数
  response => {
    // 计算请求耗时:当前时间 - 请求开始时间
    const duration = new Date() - response.config.metadata.startTime;

    // 调用日志记录函数,记录第三方服务调用的详细信息
    logThirdParty({
      service: 'payment',       // 服务名称(例如支付服务)
      endpoint: response.config.url, // 请求的端点 URL
      duration,                 // 请求耗时(毫秒)
      success: true,            // 请求是否成功
    });

    // 返回响应对象,继续后续处理
    return response;
  },
  // 错误响应的处理函数
  error => {
    // 计算请求耗时:当前时间 - 请求开始时间
    const duration = new Date() - error.config.metadata.startTime;

    // 调用日志记录函数,记录第三方服务调用的错误信息
    logThirdParty({
      service: 'sms',           // 服务名称(例如短信服务)
      endpoint: error.config.url, // 请求的端点 URL
      duration,                 // 请求耗时(毫秒)
      success: false,           // 请求是否成功
      error: error.message,     // 错误信息
    });

    // 拒绝 Promise,将错误传递给调用方
    return Promise.reject(error);
  }
);

代码说明:

  1. 创建 Axios 实例

    • const apiClient = axios.create() 创建一个自定义的 Axios 实例,用于发送 HTTP 请求。
  2. 请求拦截器

    • apiClient.interceptors.request.use(config => { ... }) 添加一个请求拦截器,用于在请求发送前修改请求配置。
    • config.metadata = { startTime: new Date() } 在请求配置中添加 metadata 对象,记录请求的开始时间。
  3. 响应拦截器

    • apiClient.interceptors.response.use(response => { ... }, error => { ... }) 添加一个响应拦截器,用于处理成功和失败的响应。
  4. 成功响应处理

    • const duration = new Date() - response.config.metadata.startTime 计算请求耗时。
    • logThirdParty({ ... }) 调用日志记录函数,记录第三方服务调用的详细信息(服务名称、端点、耗时、成功状态)。
    • return response 返回响应对象,继续后续处理。
  5. 错误响应处理

    • const duration = new Date() - error.config.metadata.startTime 计算请求耗时。
    • logThirdParty({ ... }) 调用日志记录函数,记录第三方服务调用的错误信息(服务名称、端点、耗时、成功状态、错误信息)。
    • return Promise.reject(error) 拒绝 Promise,将错误传递给调用方。

注意事项:

  • logThirdParty 是一个自定义函数,需要根据实际需求实现(如写入数据库、发送到监控系统等)。
  • 示例中 service 字段在成功和失败时分别设置为 'payment''sms',实际使用时可以根据请求的 URL 或配置动态设置。
  • 确保 axios 已正确导入(如 import axios from 'axios')。

四、数据聚合与分析平台

1. 统一上报中心

  • 接收来自各层的日志数据(前端、Nginx、后端、数据库、第三方)
  • 格式标准化(JSON 统一字段)
  • 支持批量上报(提升性能)

2. 数据存储

存储类型用途
Elasticsearch全文搜索、实时分析、日志聚合
MySQL / PostgreSQL结构化数据报表、持久化存储
Redis缓存最近请求、热数据统计
Kafka / RabbitMQ异步日志消费、削峰填谷

3. 可视化分析

工具用途
KibanaELK 可视化,查看请求分布、趋势图
GrafanaPrometheus + Metrics 实时监控
自研仪表盘展示关键指标(如 P95 耗时、错误率、Top 慢接口)

五、典型业务场景详解

场景一:页面加载缓慢问题排查

问题描述:

用户反馈首页加载慢。

分析步骤:
  1. 查看前端埋点数据:LCP 时间为 5s
  2. 查看 Nginx 日志:请求到达服务器时间为 0.2s
  3. 查看后端日志:某接口 /api/home/data 耗时 4.5s
  4. 查看数据库日志:该接口对应 SQL 耗时 4s,未命中索引
  5. 修复建议:添加索引、优化 SQL 查询

场景二:某个接口频繁超时

问题描述:

接口 /api/order/detail 响应时间不稳定,偶发超时。

分析步骤:
  1. 查看链路追踪数据:发现部分请求在调用第三方支付服务时卡住
  2. 查看第三方服务日志:发现某些请求在高峰期出现 502 错误
  3. 查看重试机制:未配置失败重试,导致用户看到错误
  4. 修复建议:增加熔断、降级、重试策略

场景三:移动端用户访问慢

问题描述:

iOS 用户反映访问慢。

分析步骤:
  1. 查看设备维度数据:iPhone 用户平均请求耗时比 PC 高 30%
  2. 查看网络维度数据:移动网络下 DNS 解析慢
  3. 查看 CDN 日志:部分区域 CDN 节点未命中
  4. 修复建议:优化 CDN 配置、预解析域名、启用 HTTP/2

场景四:A/B 测试性能对比

问题描述:

新旧两个版本接口性能差异不明。

分析步骤:
  1. 通过 Trace ID 关联请求,打上版本标签
  2. 对比两个版本接口的平均耗时、P95 耗时
  3. 发现新版接口在并发高时性能下降明显
  4. 修复建议:优化线程池配置、减少锁竞争

场景五:突发流量压垮服务

问题描述:

促销期间服务不可用。

分析步骤:
  1. 查看请求量曲线:短时间内 QPS 翻倍
  2. 查看数据库日志:大量慢查询堆积
  3. 查看链路追踪:多个接口响应时间暴涨
  4. 修复建议:限流、缓存、扩容、异步处理

六、报警机制设计

报警规则触发条件通知方式
慢接口报警P95 > 1s 或单次请求 > 3s邮件、钉钉、企业微信
错误率升高错误率 > 5% 持续 5 分钟邮件、Slack
高负载预警CPU > 90% / 内存 > 95%短信、电话
新增慢 SQL出现新的慢查询日志邮件通知 DBA
权限越权尝试无权限接口调用超过阈值邮件 + 安全告警

七、技术选型建议

模块推荐技术
前端埋点Performance API、Axios Interceptor
后端追踪OpenTelemetry、Zipkin、Jaeger
日志采集Filebeat、Logstash
数据存储Elasticsearch、Prometheus、MySQL
可视化Kibana、Grafana、自研 Dashboard
报警系统AlertManager、钉钉机器人、企业微信 Webhook

八、性能指标定义(SRE/KPI)

指标名称定义建议值
平均请求耗时所有请求的平均响应时间< 300ms
P95 请求耗时95% 的请求响应时间< 800ms
接口成功率成功请求数 / 总请求数> 99.9%
慢请求比例耗时 > 1s 的请求占比< 1%
页面 LCPLargest Contentful Paint< 2.5s
首屏加载时间页面首次可交互时间< 3s
接口错误率5xx 错误占比< 0.1%

✅ 总结

一个完整的全站请求耗时统计工具需要从前端、网络、后端、数据库、第三方等多个层面进行埋点和分析。它不仅是性能优化的基础,更是系统稳定性保障的重要组成部分。

核心要点总结如下:

  • 全链路埋点:覆盖用户端、网关、服务层、数据库、第三方服务
  • 统一数据格式:便于后续分析和聚合
  • 多维分析能力:支持按 URL、用户、设备、地区等维度分析
  • 报警机制完善:及时发现性能问题
  • 可视化展示:帮助团队快速理解性能现状

📌 附录推荐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈前端老曹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值