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

news2024/12/26 12:29:51

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.coloradmin.cn/o/2221380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux中输入和输出基本过程

目录 Linux中输入和输出基本过程 文件内核级缓冲区 何为重定向 子进程与缓冲区 手撕一个简单的shell&#xff08;版本2&#xff09; 判断重定向命令与截取 执行重定向 简单实现stdio.h中的文件相关操作 FILE结构体 fopen函数 fwrite函数 fflush函数 fclose函数 Li…

Vue+TypeScript+SpringBoot的WebSocket基础教学

成品图&#xff1a; 对WebSocket的理解&#xff08;在使用之前建议先了解Tcp&#xff0c;三次握手&#xff0c;四次挥手 &#xff09;&#xff1a; 首先页面与WebSocket建立连接、向WebSocket发送信息、后端WebSocket向所有连接上WebSoket的客户端发送当前信息。 推荐浏览网站…

燕山大学23级经济管理学院 10.18 C语言作业

燕山大学23级经济管理学院 10.18 C语言作业 文章目录 燕山大学23级经济管理学院 10.18 C语言作业1C语言的基本数据类型主要包括以下几种&#xff1a;为什么设计数据类型&#xff1f;数据类型与知识体系的对应使用数据类型时需要考虑的因素 21. 逻辑运算符2. 真值表3. 硬件实现4…

设计模式(UML图、类之间关系、设计原则)

目录 一.类的UML图 1.类的UML图 2.类之间的关系 2.1 继承关系&#xff1a; 2.2关联关系 2.2.1单项关联 2.2.2双向关联 2.2.3自关联 2.3聚合关系 2.4组合模式 2.5依赖关系 二、设计三原则 2.1单一职责原则 2.2开放封闭原则 2.3依赖倒转原则 一.类的UML图 1.类的…

考研篇——数据结构王道3.2.2_队列的顺序实现

目录 1.实现方式说明2.代码实现2.12.1.1 代码12.1.2 代码22.1.3 代码3 2.22.2.1 代码42.2.5 代码52.2.6 代码6 总结 1.实现方式说明 多在选择题中考察 队尾指针&#xff08;rear&#xff09;有两种指向方式&#xff1a; 队尾指针指向队尾元素的位置&#xff0c;队尾指针指向…

9个3D直播场景推荐

在科技日新月异的今天&#xff0c;3D直播技术正逐步渗透至文旅领域&#xff0c;为游客带来前所未有的沉浸式体验。以下是vLive虚拟直播九个精心推荐的文旅3D直播场景&#xff0c;它们不仅展现了各地独特的文化魅力&#xff0c;还通过高科技手段让游客仿佛穿越时空&#xff0c;身…

ArcGIS应用指南:多尺度渔网创建

在GIS中&#xff0c;创建渔网矢量文件是GIS中的一项常见任务&#xff0c;通过将研究区域划分为规则的网格&#xff0c;可以更精细地分析和管理城市空间数据。本文以厦门市行政区为例&#xff0c;详细介绍了如何创建不同尺度的渔网矢量网格&#xff0c;以适应不同区域的发展特点…

代码随想录算法训练营第46期Day35

leetcode.452.用最少数量的箭射爆气球 class Solution { public:static bool cmp(const vector<int>& a,const vector<int>& b){//对Xend进行排序return a[1]<b[1]; } //这个题比较好贪&#xff0c;我们每次射只需要射Xend就可以了&#xff0c;然后用此…

OpenCL内存模型

OpenCL将内存划分成主机内存和设备内存。主机内存可在主机上使用&#xff0c;其并不在OpenCL的定义范围内。使用对应的OpenCL API可以进行主机和设备的数据传输&#xff0c;或者通过共享虚拟内存接口进行内存共享。而设备内存&#xff0c;指定是能在执行内核中使用的内存空间。…

什么是不同类型的微服务测试?

大家好&#xff0c;我是锋哥。今天分享关于【什么是不同类型的微服务测试&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; 什么是不同类型的微服务测试&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 微服务架构中的测试可以分为多种类…

【ROS2】构建导航工程

1、ROS小车组成 ROS小车由三大件组成:运动底盘、ROS主控、导航传感器。 1.1 运动底盘 运动底盘的硬件由车轮、电机(带编码器)、电机驱动器、STM32控制器、电池等组成。 涉及的知识点主要为:STM32单片机程序、机器人运动学分析 1)STM32单片机程序 单片机程序框架如下:…

在Linux命令行下载Google Drive大文件(解决Google Drive下载慢的问题)

文章目录 1、使用gdown命令2、复制链接3、替换为Linux下载链接 注意&#xff1a;在Linux命令行进行 1、使用gdown命令 wget只能下载小文件&#xff0c;大文件需要用到gdown pip install gdown# 如果不能够直接安装&#xff0c;使用以下命令 git clone https://github.com/wk…

基于Spring Boot + Vue程序员云书店系统设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

MySQL9.0安装教程zip手动安装(Windows)

本章教程&#xff0c;主要介绍如何在Windows上安装MySQL9.0&#xff0c;通过zip方式进行手动安装。 一、下载MySQL压缩包 下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 二、解压MySQL压缩包 将下载好的压缩包&#xff0c;进行解压缩&#xff0c;并且将…

要让AI(任何一款绘图AI)把一个己有风格的图片画到一个实物商品上的窍门

本教程适合midjourney, comfyui, stable diffusion 己有图片 希望生成效果 我们希望&#xff0c;在一个现实世界真实IPhone手机上可以有一个这样的小魔女作为一个手机的展示&#xff0c;同时手机处于开机状态&#xff0c;在手机的屏幕上有一个这样的戴帽子的穿蓝色小披风的小…

阿里Dataworks使用循环节点和赋值节点完成对mongodb分表数据同步

背景 需求将MongoDB数据入仓MaxCompute 环境说明 MongoDB 100个Collections&#xff1a;orders_1、orders_2、…、orders_100 前期准备 1、MongoDB数据源配置 需要先保证DW和MongoDB网络是能够联通的&#xff0c;需要现在集成任务中配置MongoDB的数据源信息。 具体可以查…

Java项目-基于springboot框架的学习选课系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

Linux基础命令(入门)

linux 用户 root 用户 一个特殊的管理帐户 也被称为超级用户 root已接近完整的系统控制 对系统损害几乎有无限的能力 除非必要,不要登录为 root 普通&#xff08; 非特权 &#xff09; 用户权限有限 造成损害的能力比较有限 linux的哲学思想&#xff08;优点&#xf…

vue3学习记录-组件通信

vue3学习记录-组件通信 1.父子组件通信2.兄弟组件传值2.1 以父组件为媒介2.2 发布订阅模式2.3 使用mitt2.3.1 全局使用2.3.2 局部使用 1.父子组件通信 父组件&#xff1a; <template>父组件原有的title:{{ title }}<p>---</p><com :title"title&qu…

jmeter使用文档

文章目录 一、安装使用1、下载2、bin/jmeter.properties介绍 二、windows使用1、微调&#xff08;1&#xff09;界面样式&#xff08;2&#xff09;修改语言 2、简单使用3、各组件详解&#xff08;1&#xff09;CSV 数据文件配置&#xff08;2&#xff09;BeanShell取样器 三、…