当前位置: 首页 > article >正文

鸿蒙网络编程系列32-基于拦截器的性能监控示例

1. 拦截器简介

在Web开发中拦截器是一种非常有用的模式,它允许开发者在请求发送到服务器之前或响应返回给客户端之前执行一些预处理或后处理操作。这种机制特别适用于需要对所有网络请求或响应进行统一处理的情况,比如添加全局错误处理、请求头的修改、响应数据的格式化等,本示例将使用RCP模块提供的拦截器功能,实现对HTTP请求的性能监控,为简单起见,本示例只记录每个HTTP请求和响应的时间以及相关的状态信息,读者可以根据需要记录更多的信息并在此基础上进行深入的统计分析。

在RCP模块的API中,拦截器是以接口的形式提供的,接口名称为Interceptor,包括名称为intercept的一个方法:

intercept(context: RequestContext, next: RequestHandler): Promise<Response>

该方法第一个参数context为请求上下文,第二参数next为下一个请求处理器,可以返回Response的Promise对象,或者返回next调用handle方法的值。

2. 拦截器性能监控演示

本示例运行后的界面如图所示:

这里列出了5个可以请求的web地址,输入要请求的次数,然后单击“随机请求”按钮,应用会随机请求地址列表中的web地址,拦截器会在下方的日志区域实时显示请求性能信息,如图所示:

3. 拦截器性能监控示例编写

下面详细介绍创建该示例的步骤。
步骤1:创建Empty Ability项目。
步骤2:在module.json5配置文件加上对权限的声明:

"requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]

这里添加了访问互联网的权限。
步骤3:在Index.ets文件里添加如下的代码:

import { rcp } from '@kit.RemoteCommunicationKit';

@ObservedV2
  //拦截日志类
class RequestPerfRecord {
  @Trace public id: string = ""
  @Trace public method: string = ""
  @Trace public begin: Date = new Date()
  @Trace public end: Date = new Date()
  @Trace public requestUrl: string = ""
  @Trace public stateCode: number = -1

  constructor(request: rcp.Request) {
    this.id = request.id
    this.method = request.method
    this.begin = new Date()
    this.requestUrl = request.url.toString()
  }

  //请求耗费的秒数
  public spendTime(): number {
    return (this.end.valueOf() - this.begin.valueOf()) / 1000
  }

  public toString(): string {
    if (this.stateCode == -1) {
      return `请求地址:${this.requestUrl}\r\n请求方法:${this.method}\r\n` +
        `请求开始时间:${this.begin.toLocaleString()} \r\n`
    } else {
      return `请求地址:${this.requestUrl}\r\n请求方法:${this.method}\r\n` +
        `请求开始时间:${this.begin.toLocaleString()}\r\n响应时间:${this.end.toLocaleString()} \r\n` +
        `请求响应耗时:${this.spendTime()}秒\r\n` +
        `响应状态码:${this.stateCode}\r\n`
    }
  }
}

@Entry
@ComponentV2
struct Index {
  @Local title: string = '基于拦截器的HTTP请求性能监控';
  //请求地址列表
  @Local requestUrlList: Array<string> =
    ["https://www.baidu.com", "https://www.baidu.com/nopage.html", "https://www.aliyun.com/"
      , "https://developer.huawei.com/consumer/cn/forum/", "https://www.zhihu.com/"]
  //请求次数
  @Local requestTimes: number = 5
  //拦截日志列表
  @Local requestPerRecordList: Array<RequestPerfRecord> = new Array()
  scroller: Scroller = new Scroller()

  build() {
    Row() {
      Column() {
        Text(this.title)
          .fontSize(14)
          .fontWeight(FontWeight.Bold)
          .width('100%')
          .textAlign(TextAlign.Center)
          .padding(5)

        Text("请求地址列表:")
          .fontSize(14)
          .width('100%')
          .padding(5)

        List({ space: 5, initialIndex: 0 }) {
          ForEach(this.requestUrlList, (item: string) => {
            ListItem() {
              Text(item)
                .fontSize(13)
            }
          }, (item: string) => item)
        }.width('100%')
        .padding(5)

        Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
          Text("请求次数:")
            .fontSize(14)
            .width(80)

          Counter() {
            Text(this.requestTimes.toString())
          }
          .onInc(() => {
            this.requestTimes++
          })
          .onDec(() => {
            this.requestTimes--
          })
          .width(140)
          .padding(10)
          .height(50)

          Button("随机请求")
            .onClick(() => {
              this.requestTest()
            })
            .width(100)
            .fontSize(14)
        }
        .width('100%')
        .padding(5)

        Scroll(this.scroller) {
          List({ space: 5, initialIndex: 0 }) {
            ForEach(this.requestPerRecordList, (item: RequestPerfRecord) => {
              ListItem() {
                Text(item.toString())
                  .fontSize(13)
              }
            }, (item: string) => item)
          }.width('100%')
          .padding(5)
        }
        .align(Alignment.Top)
        .backgroundColor(0xeeeeee)
        .height(300)
        .flexGrow(1)
        .scrollable(ScrollDirection.Vertical)
        .scrollBar(BarState.On)
        .scrollBarWidth(20)
      }
      .width('100%')
      .justifyContent(FlexAlign.Start)
      .height('100%')
    }
    .height('100%')
  }

  async requestTest() {
    let cfg: rcp.SessionConfiguration = {
      interceptors: [new PerfInterceptor(this.requestPerRecordList)]
    }

    const session = rcp.createSession(cfg);

    for (let i = 0; i < this.requestTimes; i++) {
      let index = Math.floor(Math.random() * this.requestUrlList.length)
      await session.get(this.requestUrlList[index])
      let sleepTime = Math.random() * 5 + 1
      await sleep(sleepTime)
    }
  }
}

//休眠指定的毫秒数
function sleep(time: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, time));
}

//性能监控拦截器
class PerfInterceptor implements rcp.Interceptor {
  requestPerList: Array<RequestPerfRecord>

  constructor(requestPerList: Array<RequestPerfRecord>) {
    this.requestPerList = requestPerList
  }

  //拦截方法
  async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
    let record = new RequestPerfRecord(context.request)
    this.requestPerList.push(record)
    const promise = next.handle(context);
    promise.then((resp) => {
      record.stateCode = resp.statusCode;
      record.end = new Date()
    });
    return promise;
  }
}

步骤4:编译运行,可以使用模拟器或者真机。

步骤5:按照本节第1部分“拦截器性能监控演示”操作即可。

4. 代码分析

本示例的关键点在于记录HTTP请求及响应的时间,这是通过拦截器的intercept方法实现的,该方法首先记录当前的时间,也就是请求发起的时间,然后调用next的handle方法,该方法会发起HTTP请求并返回响应,在该方法的响应处理回调函数中,再次记录当时的时间,也就是响应返回的时间,这样就拿到了最关键的两个时间信息,具体代码如下所示:

async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
    let record = new RequestPerfRecord(context.request)
    this.requestPerList.push(record)
    const promise = next.handle(context);
    promise.then((resp) => {
      record.stateCode = resp.statusCode;
      record.end = new Date()
    });
    return promise;
  }

另外,因为需要把记录的信息在应用界面上展示,所以把状态变量requestPerRecordList传递到了拦截器实例中,这样拦截器生成的HTTP请求日志信息也就保存到了requestPerRecordList变量中,从而可以在界面上随时看到拦截日志。

(本文作者原创,除非明确授权禁止转载)

本文源码地址:
https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/rcp/HttpRequestMonitor

本系列源码地址:
https://gitee.com/zl3624/harmonyos_network_samples


http://www.kler.cn/news/368084.html

相关文章:

  • Spring Cloud 微服务综述 | 含服务调用最佳实践
  • 炒股VS炒CSGO游戏装备,哪个更好做
  • Next.js、Prisma 和 MySQL 实践示例
  • 读《认知觉醒》:浅谈费曼技巧
  • ThinkPhp配置中间件解决跨域问题
  • 《Pyhon入门:07 map与filter函数的常用用法》
  • unity中GameObject介绍
  • unity 导入的模型设置详谈
  • 【ShuQiHere】Linux 系统中的硬盘管理详解:命令与技巧
  • C++ | Leetcode C++题解之 第508题出现次数最多的子树元素和
  • Day 53 图论五
  • nginx 修改配置
  • 正则表达式(Regular Expression, Regex)详解
  • linux中的PATH环境变量
  • 【笔记】Diffusion Model 扩散过程(熵增过程:从有序变为无序):在原始分布上逐步的加高斯噪声,加到最后这个分布就变成一个各项独立的高斯分布
  • [Linux网络编程]05-TCP状态和端口复用,shutdown函数(主动方建立/关闭连接状态,被动方建立/关闭连接状态,2MSL时长,TCP其他状态)
  • protobuf序列化
  • 解读AVL树:平衡二叉搜索树的奥秘
  • python 爬虫 入门 五、抓取图片、视频
  • 建造者设计模式
  • 基于知识图谱的苹果病虫害知识图谱问答
  • redis详细教程(2.List教程)
  • 如何快速开发一套基于Java的诊所管理系统?
  • C++设计模式——Factory Method工厂方法模式
  • C#文件内容检索的功能
  • P11232 [CSP-S 2024] 超速检测(民间数据)