HarmonyOS开发网络性能监控:请求耗时分析

看不见的性能问题,用数据说话

一、背景与动机:为什么需要网络性能监控?

用户反馈:"App好慢啊!"

你问:"哪里慢?"

用户:"就是慢!"

没有监控数据,你永远不知道慢在哪里

  • 是DNS解析慢?
  • 是TCP连接慢?
  • 是服务器响应慢?
  • 还是数据解析慢?

网络性能监控要解决的就是这个问题:

  1. 定位瓶颈:精确到每个阶段的耗时
  2. 发现问题:自动检测异常请求
  3. 优化依据:用数据指导优化
  4. 质量保障:持续监控线上性能

性能监控的关键指标

指标说明重要性
DNS耗时域名解析时间⭐⭐⭐
TCP耗时连接建立时间⭐⭐⭐
TLS耗时HTTPS握手时间⭐⭐⭐
首字节时间(TTFB)服务器响应时间⭐⭐⭐⭐⭐
内容传输时间数据下载时间⭐⭐⭐⭐
总耗时完整请求时间⭐⭐⭐⭐⭐

二、核心原理:请求生命周期追踪

每个HTTP请求都有完整的生命周期,我们需要追踪每个阶段:

flowchart TB
    A[请求开始<br/>记录startTime] --> B[DNS解析<br/>记录dnsTime]
    B --> C[TCP连接<br/>recordTcpTime]
    C --> D[TLS握手<br/>recordTlsTime]
    D --> E[发送请求<br/>recordSendTime]
    E --> F[等待响应<br/>recordWaitTime]
    F --> G[接收数据<br/>recordReceiveTime]
    G --> H[数据解析<br/>recordParseTime]
    H --> I[请求结束<br/>recordEndTime]
  
    I --> J[计算各阶段耗时]
    J --> K[生成性能报告]
    K --> L{是否异常?}
  
    L -->|是| M[上报异常]
    L -->|否| N[记录日志]
  
    M --> O[告警通知]
    N --> P[存储统计]
  
    classDef primary fill:#4A90E2,stroke:#2E5C8A,color:#fff
    classDef warning fill:#F5A623,stroke:#C17D10,color:#fff
    classDef error fill:#E74C3C,stroke:#C0392B,color:#fff
    classDef info fill:#7ED321,stroke:#5BA318,color:#fff
  
    class A,B,C,D,E,F,G,H,I,J,K primary
    class L info
    class M,O error
    class N,P warning

时间计算

  • DNS耗时 = TCP开始时间 - 请求开始时间
  • TCP耗时 = TLS开始时间 - TCP开始时间
  • TLS耗时 = 请求发送时间 - TLS开始时间
  • TTFB = 首字节时间 - 请求发送时间
  • 传输耗时 = 请求结束时间 - 首字节时间
  • 总耗时 = 请求结束时间 - 请求开始时间

三、代码实战:完整性能监控系统

示例1:性能指标定义

// network/monitor/types.ets

/**
 * 请求性能指标
 */
export interface RequestMetrics {
  /** 请求ID */
  requestId: string;
  /** 请求URL */
  url: string;
  /** HTTP方法 */
  method: string;
  
  // 时间戳(毫秒)
  /** 请求开始时间 */
  startTime: number;
  /** DNS解析完成时间 */
  dnsTime?: number;
  /** TCP连接完成时间 */
  tcpTime?: number;
  /** TLS握手完成时间 */
  tlsTime?: number;
  /** 请求发送完成时间 */
  sendTime?: number;
  /** 首字节到达时间 */
  firstByteTime?: number;
  /** 响应接收完成时间 */
  receiveTime?: number;
  /** 数据解析完成时间 */
  parseTime?: number;
  /** 请求结束时间 */
  endTime?: number;
  
  // 耗时(毫秒)
  /** DNS解析耗时 */
  dnsDuration?: number;
  /** TCP连接耗时 */
  tcpDuration?: number;
  /** TLS握手耗时 */
  tlsDuration?: number;
  /** 请求发送耗时 */
  sendDuration?: number;
  /** 等待首字节耗时(TTFB) */
  ttfb?: number;
  /** 内容传输耗时 */
  downloadDuration?: number;
  /** 数据解析耗时 */
  parseDuration?: number;
  /** 总耗时 */
  totalDuration?: number;
  
  // 其他信息
  /** HTTP状态码 */
  statusCode?: number;
  /** 响应大小(字节) */
  responseSize?: number;
  /** 请求大小(字节) */
  requestSize?: number;
  /** 是否命中缓存 */
  fromCache?: boolean;
  /** 是否成功 */
  success?: boolean;
  /** 错误信息 */
  error?: string;
}

/**
 * 性能统计报告
 */
export interface PerformanceReport {
  /** 时间范围 */
  timeRange: {
    start: number;
    end: number;
  };
  /** 请求数量 */
  totalRequests: number;
  /** 成功请求数 */
  successRequests: number;
  /** 失败请求数 */
  failedRequests: number;
  /** 平均耗时 */
  avgDuration: number;
  /** P50耗时 */
  p50Duration: number;
  /** P95耗时 */
  p95Duration: number;
  /** P99耗时 */
  p99Duration: number;
  /** 平均TTFB */
  avgTtfb: number;
  /** 总传输大小 */
  totalBytes: number;
  /** 按URL分组统计 */
  urlStats: Map<string, UrlStats>;
}

/**
 * URL统计
 */
export interface UrlStats {
  url: string;
  count: number;
  avgDuration: number;
  maxDuration: number;
  minDuration: number;
  errorRate: number;
}

/**
 * 性能告警配置
 */
export interface PerformanceAlertConfig {
  /** 慢请求阈值(毫秒) */
  slowThreshold?: number;
  /** 失败率阈值 */
  errorRateThreshold?: number;
  /** TTFB阈值 */
  ttfbThreshold?: number;
  /** 告警回调 */
  onAlert?: (alert: PerformanceAlert) => void;
}

/**
 * 性能告警
 */
export interface PerformanceAlert {
  type: 'slow' | 'error' | 'ttfb';
  message: string;
  metrics: RequestMetrics;
  timestamp: number;
}

示例2:性能追踪器

// network/monitor/PerformanceTracker.ets
import { RequestMetrics, PerformanceAlert, PerformanceAlertConfig } from './types';

/**
 * 性能追踪器
 * 追踪单个请求的性能指标
 */
export class PerformanceTracker {
  private metrics: RequestMetrics;
  private alertConfig: PerformanceAlertConfig;

  constructor(
    requestId: string,
    url: string,
    method: string,
    alertConfig?: PerformanceAlertConfig
  ) {
    this.metrics = {
      requestId,
      url,
      method,
      startTime: Date.now(),
      success: false
    };
    this.alertConfig = alertConfig || {};
  }

  /**
   * 标记DNS解析完成
   */
  markDnsComplete(): void {
    this.metrics.dnsTime = Date.now();
    this.metrics.dnsDuration = this.metrics.dnsTime - this.metrics.startTime;
  }

  /**
   * 标记TCP连接完成
   */
  markTcpComplete(): void {
    this.metrics.tcpTime = Date.now();
    this.metrics.tcpDuration = this.metrics.tcpTime - 
      (this.metrics.dnsTime || this.metrics.startTime);
  }

  /**
   * 标记TLS握手完成
   */
  markTlsComplete(): void {
    this.metrics.tlsTime = Date.now();
    this.metrics.tlsDuration = this.metrics.tlsTime - 
      (this.metrics.tcpTime || this.metrics.dnsTime || this.metrics.startTime);
  }

  /**
   * 标记请求发送完成
   */
  markSendComplete(requestSize?: number): void {
    this.metrics.sendTime = Date.now();
    this.metrics.sendDuration = this.metrics.sendTime - 
      (this.metrics.tlsTime || this.metrics.tcpTime || this.metrics.startTime);
    this.metrics.requestSize = requestSize;
  }

  /**
   * 标记首字节到达
   */
  markFirstByte(): void {
    this.metrics.firstByteTime = Date.now();
    this.metrics.ttfb = this.metrics.firstByteTime - 
      (this.metrics.sendTime || this.metrics.tlsTime || this.metrics.startTime);
  }

  /**
   * 标记响应接收完成
   */
  markReceiveComplete(responseSize?: number): void {
    this.metrics.receiveTime = Date.now();
    this.metrics.downloadDuration = this.metrics.receiveTime - 
      (this.metrics.firstByteTime || this.metrics.sendTime || this.metrics.startTime);
    this.metrics.responseSize = responseSize;
  }

  /**
   * 标记数据解析完成
   */
  markParseComplete(): void {
    this.metrics.parseTime = Date.now();
    this.metrics.parseDuration = this.metrics.parseTime - 
      (this.metrics.receiveTime || this.metrics.firstByteTime || this.metrics.startTime);
  }

  /**
   * 标记请求成功
   */
  markSuccess(statusCode: number): void {
    this.metrics.endTime = Date.now();
    this.metrics.statusCode = statusCode;
    this.metrics.success = true;
    this.metrics.totalDuration = this.metrics.endTime - this.metrics.startTime;

    this.checkAlert();
  }

  /**
   * 标记请求失败
   */
  markFailed(error: string): void {
    this.metrics.endTime = Date.now();
    this.metrics.error = error;
    this.metrics.success = false;
    this.metrics.totalDuration = this.metrics.endTime - this.metrics.startTime;

    this.checkAlert();
  }

  /**
   * 标记缓存命中
   */
  markCacheHit(): void {
    this.metrics.fromCache = true;
    this.metrics.endTime = Date.now();
    this.metrics.totalDuration = this.metrics.endTime - this.metrics.startTime;
  }

  /**
   * 获取性能指标
   */
  getMetrics(): RequestMetrics {
    return this.metrics;
  }

  /**
   * 检查是否需要告警
   * @private
   */
  private checkAlert(): void {
    if (!this.alertConfig.onAlert) return;

    // 检查慢请求
    if (this.alertConfig.slowThreshold && this.metrics.totalDuration) {
      if (this.metrics.totalDuration > this.alertConfig.slowThreshold) {
        this.alertConfig.onAlert({
          type: 'slow',
          message: `慢请求: ${this.metrics.url} 耗时 ${this.metrics.totalDuration}ms`,
          metrics: this.metrics,
          timestamp: Date.now()
        });
      }
    }

    // 检查TTFB
    if (this.alertConfig.ttfbThreshold && this.metrics.ttfb) {
      if (this.metrics.ttfb > this.alertConfig.ttfbThreshold) {
        this.alertConfig.onAlert({
          type: 'ttfb',
          message: `TTFB过高: ${this.metrics.url} TTFB ${this.metrics.ttfb}ms`,
          metrics: this.metrics,
          timestamp: Date.now()
        });
      }
    }

    // 检查失败
    if (!this.metrics.success) {
      this.alertConfig.onAlert({
        type: 'error',
        message: `请求失败: ${this.metrics.url} ${this.metrics.error}`,
        metrics: this.metrics,
        timestamp: Date.now()
      });
    }
  }
}

示例3:性能监控管理器

// network/monitor/PerformanceMonitor.ets
import { RequestMetrics, PerformanceReport, UrlStats, PerformanceAlertConfig } from './types';
import { PerformanceTracker } from './PerformanceTracker';

/**
 * 性能监控管理器
 * 汇总和分析性能数据
 */
export class PerformanceMonitor {
  private metrics: RequestMetrics[] = [];
  private maxRecords: number = 1000; // 最大记录数
  private alertConfig: PerformanceAlertConfig;
  private enabled: boolean = true;

  constructor(config?: PerformanceAlertConfig & { enabled?: boolean; maxRecords?: number }) {
    this.alertConfig = config || {};
    this.enabled = config?.enabled ?? true;
    this.maxRecords = config?.maxRecords ?? 1000;
  }

  /**
   * 创建性能追踪器
   */
  createTracker(url: string, method: string): PerformanceTracker {
    const requestId = this.generateRequestId();
    return new PerformanceTracker(requestId, url, method, this.alertConfig);
  }

  /**
   * 记录性能指标
   */
  record(metrics: RequestMetrics): void {
    if (!this.enabled) return;

    this.metrics.push(metrics);

    // 超过最大记录数,移除旧记录
    if (this.metrics.length > this.maxRecords) {
      this.metrics.shift();
    }

    // 记录日志
    this.logMetrics(metrics);
  }

  /**
   * 生成性能报告
   */
  generateReport(timeRange?: { start: number; end: number }): PerformanceReport {
    // 过滤时间范围
    let filteredMetrics = this.metrics;
    if (timeRange) {
      filteredMetrics = this.metrics.filter(m => 
        m.startTime >= timeRange.start && m.startTime <= timeRange.end
      );
    }

    // 计算统计指标
    const durations = filteredMetrics
      .filter(m => m.totalDuration !== undefined)
      .map(m => m.totalDuration!);
  
    const ttfbs = filteredMetrics
      .filter(m => m.ttfb !== undefined)
      .map(m => m.ttfb!);

    const report: PerformanceReport = {
      timeRange: timeRange || {
        start: filteredMetrics[0]?.startTime || Date.now(),
        end: filteredMetrics[filteredMetrics.length - 1]?.endTime || Date.now()
      },
      totalRequests: filteredMetrics.length,
      successRequests: filteredMetrics.filter(m => m.success).length,
      failedRequests: filteredMetrics.filter(m => !m.success).length,
      avgDuration: this.average(durations),
      p50Duration: this.percentile(durations, 50),
      p95Duration: this.percentile(durations, 95),
      p99Duration: this.percentile(durations, 99),
      avgTtfb: this.average(ttfbs),
      totalBytes: filteredMetrics.reduce((sum, m) => sum + (m.responseSize || 0), 0),
      urlStats: this.groupByUrl(filteredMetrics)
    };

    return report;
  }

  /**
   * 获取慢请求
   */
  getSlowRequests(threshold: number): RequestMetrics[] {
    return this.metrics.filter(m => 
      m.totalDuration !== undefined && m.totalDuration > threshold
    );
  }

  /**
   * 获取失败请求
   */
  getFailedRequests(): RequestMetrics[] {
    return this.metrics.filter(m => !m.success);
  }

  /**
   * 清空记录
   */
  clear(): void {
    this.metrics = [];
  }

  /**
   * 启用/禁用监控
   */
  setEnabled(enabled: boolean): void {
    this.enabled = enabled;
  }

  /**
   * 按URL分组统计
   * @private
   */
  private groupByUrl(metrics: RequestMetrics[]): Map<string, UrlStats> {
    const groups = new Map<string, RequestMetrics[]>();
  
    for (const m of metrics) {
      const existing = groups.get(m.url) || [];
      existing.push(m);
      groups.set(m.url, existing);
    }

    const stats = new Map<string, UrlStats>();
    groups.forEach((items, url) => {
      const durations = items
        .filter(m => m.totalDuration !== undefined)
        .map(m => m.totalDuration!);
    
      const errorCount = items.filter(m => !m.success).length;

      stats.set(url, {
        url,
        count: items.length,
        avgDuration: this.average(durations),
        maxDuration: Math.max(...durations),
        minDuration: Math.min(...durations),
        errorRate: errorCount / items.length
      });
    });

    return stats;
  }

  /**
   * 计算平均值
   * @private
   */
  private average(values: number[]): number {
    if (values.length === 0) return 0;
    return values.reduce((sum, v) => sum + v, 0) / values.length;
  }

  /**
   * 计算百分位数
   * @private
   */
  private percentile(values: number[], p: number): number {
    if (values.length === 0) return 0;
  
    const sorted = [...values].sort((a, b) => a - b);
    const index = Math.ceil((p / 100) * sorted.length) - 1;
    return sorted[Math.max(0, index)];
  }

  /**
   * 生成请求ID
   * @private
   */
  private generateRequestId(): string {
    return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  /**
   * 记录日志
   * @private
   */
  private logMetrics(metrics: RequestMetrics): void {
    const logLevel = metrics.success ? 'info' : 'warn';
    console[logLevel](`[PerformanceMonitor] ${metrics.method} ${metrics.url}`, {
      totalDuration: metrics.totalDuration,
      ttfb: metrics.ttfb,
      statusCode: metrics.statusCode,
      success: metrics.success
    });
  }
}

示例4:性能监控拦截器

// network/monitor/PerformanceInterceptor.ets
import { Interceptor, RequestConfig, Response } from '../types';
import { PerformanceMonitor } from './PerformanceMonitor';
import { PerformanceTracker } from './PerformanceTracker';

/**
 * 性能监控拦截器
 * 自动追踪所有请求的性能
 */
export class PerformanceInterceptor implements Interceptor {
  private monitor: PerformanceMonitor;

  constructor(monitor: PerformanceMonitor) {
    this.monitor = monitor;
  }

  /**
   * 请求拦截:创建性能追踪器
   */
  async beforeRequest(config: RequestConfig): Promise<RequestConfig> {
    // 创建性能追踪器
    const tracker = this.monitor.createTracker(
      config.url,
      config.method || 'GET'
    );

    // 保存到配置中
    (config as any).__performanceTracker = tracker;

    return config;
  }

  /**
   * 响应拦截:记录性能指标
   */
  async afterResponse<T>(response: Response<T>): Promise<Response<T>> {
    const tracker: PerformanceTracker = (response.config as any).__performanceTracker;

    if (!tracker) {
      return response;
    }

    try {
      // 标记首字节到达
      tracker.markFirstByte();

      // 标记响应接收完成
      const responseSize = this.estimateSize(response.data);
      tracker.markReceiveComplete(responseSize);

      // 标记数据解析完成
      tracker.markParseComplete();

      // 标记成功
      tracker.markSuccess(response.status);

      // 记录到监控器
      const metrics = tracker.getMetrics();
      this.monitor.record(metrics);
    } catch (error) {
      console.error('[PerformanceInterceptor] 记录性能失败:', error);
    }

    return response;
  }

  /**
   * 错误处理
   */
  handleError(config: RequestConfig, error: any): void {
    const tracker: PerformanceTracker = (config as any).__performanceTracker;

    if (tracker) {
      tracker.markFailed(error.message || 'Unknown error');
      const metrics = tracker.getMetrics();
      this.monitor.record(metrics);
    }
  }

  /**
   * 估算数据大小
   * @private
   */
  private estimateSize(data: any): number {
    if (!data) return 0;
  
    if (typeof data === 'string') {
      return data.length * 2; // UTF-16
    }
  
    if (data instanceof Uint8Array) {
      return data.length;
    }
  
    try {
      return JSON.stringify(data).length * 2;
    } catch {
      return 0;
    }
  }
}

示例5:性能报告可视化

// components/PerformanceReportPanel.ets
import { PerformanceMonitor } from '../network/monitor/PerformanceMonitor';
import { PerformanceReport } from '../network/monitor/types';

/**
 * 性能报告面板
 * 可视化展示性能数据
 */
@Component
export struct PerformanceReportPanel {
  @State monitor: PerformanceMonitor | null = null;
  @State report: PerformanceReport | null = null;
  @State loading: boolean = false;

  aboutToAppear() {
    this.loadReport();
  }

  /**
   * 加载性能报告
   */
  async loadReport() {
    if (!this.monitor) return;

    this.loading = true;
  
    try {
      // 生成最近1小时的报告
      const end = Date.now();
      const start = end - 60 * 60 * 1000;
      this.report = this.monitor.generateReport({ start, end });
    } catch (error) {
      console.error('加载报告失败:', error);
    } finally {
      this.loading = false;
    }
  }

  build() {
    Column() {
      // 标题
      Row() {
        Text('性能报告')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .layoutWeight(1)

        Button('刷新')
          .type(ButtonType.Capsule)
          .height(32)
          .onClick(() => {
            this.loadReport();
          })
      }
      .width('100%')
      .padding(16)

      if (this.loading) {
        LoadingProgress()
          .width(48)
          .height(48)
      } else if (this.report) {
        // 概览卡片
        this.overviewCard();

        // 耗时分布
        this.durationChart();

        // URL统计
        this.urlStatsList();
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  /**
   * 概览卡片
   */
  @Builder
  overviewCard() {
    Column() {
      Row() {
        this.statItem('总请求', this.report!.totalRequests.toString());
        this.statItem('成功', this.report!.successRequests.toString());
        this.statItem('失败', this.report!.failedRequests.toString());
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)

      Row() {
        this.statItem('平均耗时', `${this.report!.avgDuration.toFixed(0)}ms`);
        this.statItem('P95耗时', `${this.report!.p95Duration.toFixed(0)}ms`);
        this.statItem('P99耗时', `${this.report!.p99Duration.toFixed(0)}ms`);
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
      .margin({ top: 16 })

      Row() {
        this.statItem('平均TTFB', `${this.report!.avgTtfb.toFixed(0)}ms`);
        this.statItem('总流量', this.formatBytes(this.report!.totalBytes));
        this.statItem('', '');
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
      .margin({ top: 16 })
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(8)
    .margin({ left: 16, right: 16, top: 8 })
  }

  /**
   * 统计项
   */
  @Builder
  statItem(label: string, value: string) {
    Column() {
      Text(value)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#4A90E2')

      Text(label)
        .fontSize(12)
        .fontColor('#999999')
        .margin({ top: 4 })
    }
    .alignItems(HorizontalAlign.Center)
  }

  /**
   * 耗时分布图
   */
  @Builder
  durationChart() {
    Column() {
      Text('耗时分布')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .width('100%')

      // 简化示例:使用条形图展示
      Row() {
        this.barItem('P50', this.report!.p50Duration, this.report!.p99Duration);
        this.barItem('P95', this.report!.p95Duration, this.report!.p99Duration);
        this.barItem('P99', this.report!.p99Duration, this.report!.p99Duration);
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceEvenly)
      .margin({ top: 16 })
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(8)
    .margin({ left: 16, right: 16, top: 16 })
  }

  /**
   * 条形项
   */
  @Builder
  barItem(label: string, value: number, maxValue: number) {
    Column() {
      Text(`${value.toFixed(0)}ms`)
        .fontSize(12)
        .fontColor('#666666')

      Column()
        .width(40)
        .height((value / maxValue) * 100)
        .backgroundColor('#4A90E2')
        .borderRadius(4)
        .margin({ top: 4, bottom: 4 })

      Text(label)
        .fontSize(12)
        .fontColor('#999999')
    }
    .alignItems(HorizontalAlign.Center)
  }

  /**
   * URL统计列表
   */
  @Builder
  urlStatsList() {
    Column() {
      Text('URL统计')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .width('100%')

      List() {
        ForEach(Array.from(this.report!.urlStats.values()), (stats: any) => {
          ListItem() {
            this.urlStatsItem(stats);
          }
        })
      }
      .width('100%')
      .height(200)
      .margin({ top: 16 })
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(8)
    .margin({ left: 16, right: 16, top: 16, bottom: 16 })
  }

  /**
   * URL统计项
   */
  @Builder
  urlStatsItem(stats: any) {
    Row() {
      Column() {
        Text(stats.url)
          .fontSize(14)
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        Text(`请求${stats.count}次 | 失败率${(stats.errorRate * 100).toFixed(1)}%`)
          .fontSize(12)
          .fontColor('#999999')
          .margin({ top: 4 })
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)

      Text(`${stats.avgDuration.toFixed(0)}ms`)
        .fontSize(14)
        .fontColor('#4A90E2')
    }
    .width('100%')
    .padding({ top: 8, bottom: 8 })
    .border({ width: { bottom: 1 }, color: '#EEEEEE' })
  }

  /**
   * 格式化字节数
   */
  private formatBytes(bytes: number): string {
    if (bytes < 1024) return `${bytes}B`;
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
    return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
  }
}

示例6:性能数据上报

// network/monitor/PerformanceReporter.ets
import { PerformanceReport, RequestMetrics } from './types';

/**
 * 性能数据上报器
 * 将性能数据上报到服务器
 */
export class PerformanceReporter {
  private reportUrl: string;
  private batchSize: number = 50;
  private batch: RequestMetrics[] = [];
  private timer: number = -1;

  constructor(reportUrl: string) {
    this.reportUrl = reportUrl;
  }

  /**
   * 启动定时上报
   */
  start(interval: number = 60000): void {
    this.timer = setInterval(() => {
      this.flush();
    }, interval);

    console.info(`[PerformanceReporter] 启动定时上报,间隔: ${interval}ms`);
  }

  /**
   * 停止定时上报
   */
  stop(): void {
    if (this.timer !== -1) {
      clearInterval(this.timer);
      this.timer = -1;
    }
    // 最后一次上报
    this.flush();
  }

  /**
   * 添加性能数据
   */
  add(metrics: RequestMetrics): void {
    this.batch.push(metrics);

    // 达到批次大小,立即上报
    if (this.batch.length >= this.batchSize) {
      this.flush();
    }
  }

  /**
   * 立即上报
   */
  async flush(): Promise<void> {
    if (this.batch.length === 0) return;

    const data = [...this.batch];
    this.batch = [];

    try {
      await this.send(data);
      console.info(`[PerformanceReporter] 上报成功: ${data.length}条`);
    } catch (error) {
      console.error('[PerformanceReporter] 上报失败:', error);
      // 上报失败,放回队列
      this.batch = [...data, ...this.batch];
    }
  }

  /**
   * 发送数据
   * @private
   */
  private async send(data: RequestMetrics[]): Promise<void> {
    // 使用原生HTTP发送,避免循环依赖
    const http = await import('@ohos.net.http');
    const httpRequest = http.createHttp();

    try {
      await httpRequest.request(this.reportUrl, {
        method: http.RequestMethod.POST,
        header: { 'Content-Type': 'application/json' },
        extraData: {
          appId: 'your-app-id',
          timestamp: Date.now(),
          metrics: data
        }
      });
    } finally {
      httpRequest.destroy();
    }
  }
}

四、踩坑与注意事项

坑1:性能监控影响性能

问题:监控本身也有开销,可能影响请求性能。

解决:异步记录 + 采样:

// 采样:只监控10%的请求
if (Math.random() < 0.1) {
  tracker = this.monitor.createTracker(url, method);
}

// 异步记录
setTimeout(() => {
  this.monitor.record(metrics);
}, 0);

坑2:内存占用过大

问题:记录太多性能数据,内存占用过大。

解决:限制记录数 + 定期清理:

// 限制最大记录数
if (this.metrics.length > this.maxRecords) {
  this.metrics.shift(); // 移除最旧的
}

// 定期清理(保留最近1小时)
const oneHourAgo = Date.now() - 60 * 60 * 1000;
this.metrics = this.metrics.filter(m => m.startTime > oneHourAgo);

坑3:时间戳不精确

问题:Date.now() 精度只有毫秒,无法测量微秒级耗时。

解决:使用 performance.now()(如果支持):

// 使用高精度时间
const start = performance?.now() || Date.now();
// ...
const duration = (performance?.now() || Date.now()) - start;

坑4:上报失败丢失数据

问题:上报失败后,数据丢失。

解决:本地缓存 + 重试:

// 上报失败,存入本地
if (error) {
  await localCache.save('failed-reports', data);
}

// 下次启动时重试
const failedReports = await localCache.get('failed-reports');
if (failedReports.length > 0) {
  await reporter.send(failedReports);
}

五、HarmonyOS 6 适配要点

1. HiChecker 性能检测

// HarmonyOS 6 提供HiChecker检测卡顿
import { hiChecker } from '@kit.PerformanceAnalysisKit';

// 开启卡顿检测
hiChecker.enableRule(hiChecker.RULE_CHECK_SLOW_BLOCK);

// 监听卡顿事件
hiChecker.on('slowBlock', (info) => {
  console.warn('检测到卡顿:', info);
  // 上报卡顿信息
});

2. HiTrace 链路追踪

// HarmonyOS 6 提供分布式链路追踪
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit';

// 开始追踪
const traceId = hiTraceMeter.startTrace('http-request', 1);

// 设置追踪信息
hiTraceMeter.setTraceParams(traceId, {
  url: request.url,
  method: request.method
});

// 结束追踪
hiTraceMeter.finishTrace(traceId);

3. 实时性能监控

// HarmonyOS 6 支持实时性能监控
import { profiler } from '@kit.ProfilerKit';

// 开始性能采集
profiler.start({
  cpu: true,
  memory: true,
  network: true
});

// 获取性能快照
const snapshot = await profiler.getSnapshot();
console.log('CPU使用率:', snapshot.cpu.usage);
console.log('内存使用:', snapshot.memory.used);
console.log('网络流量:', snapshot.network.bytes);

六、总结一下下

性能监控是应用的"体检报告",让性能问题无处遁形:

监控维度关键指标优化方向
DNSDNS耗时DNS预解析、HTTPDNS
连接TCP/TLS耗时连接复用、HTTP2
传输TTFB、下载耗时CDN、压缩、缓存
解析解析耗时数据格式、流式解析
整体总耗时、成功率全链路优化

记住几个原则:

  • ✅ 监控要全面,覆盖所有请求
  • ✅ 数据要精确,追踪每个阶段
  • ✅ 告警要及时,发现问题立即通知
  • ✅ 上报要可靠,失败不丢数据
  • ✅ 开销要小,监控不影响性能

💡 最佳实践提示:建议在开发环境开启详细监控,生产环境只监控关键指标和采样数据,平衡监控效果和性能开销。

蓝胖子样样好
79 声望702 粉丝

Never give up,and you will be successful