当前位置:   article > 正文

流量回放系统的设计与实现--流量回放模块_流量回放自定义规则比对结果

流量回放自定义规则比对结果

架构设计

流程回放功能数据流转示意图

图片

 实现效果

录制:

图片

回放:

图片

功能设计

流量回放模块主要包含的功能是:

  • 任务信息列表

  • 创建回放任务

  • 修改任务信息

  • 执行任务

  • 回放对比结果

  • 查看任务详情信息

  • 流量用例列表

  • 用例详情、编辑

问题与解决方案

1队列中很多流量,如何获取指定的部分流量?

解决方案:通过对mitmproxy的二次开发,我们增加了根据指定列表id的dump接口。前端在列表选择的流量id,直接调用暴露的api接口,遍历获取到的id,自定义过滤获取指定的流量文件,同时解析流量数据并入库保存。此时的流量文件保存路径也会入库,以便于后续回放任务指定流量文件。

2回放对比时,部分请求流量很大,对比起来很慢如何处理?

解决方案:通过全量对比返回数据的形式是不可取的,特别是流量数据大时,很容易造成前端界面的卡死。这里我们通过对返回数据建立hash值的方式,直接对比2次的hash值来获得回放的对比结果,大大提高数据对比模块的性能。

3回放时如何让新的流量与老的录制流量进行关联?

解决方案:此种基于mitmproxy自身流量文件的回放方式,回放时的request流量序列id是不变的,由于在生成流量用例时已经将老的流量返回值入库保存,后续回放时通过中间插件解析并根据id也对新的response流量进行保存,通过唯一的id进行了关联。

4流量回放时如何自定义去修改请求?

解决方案:这个功能平台目前没有开发实现,但是前期已经调研尝试过,可以过开发中间件脚本对流量进行自定义的编辑处理。

核心代码设计

前段对比模块

这里采用的是 react-diff-viewer 库,组件化剥离了数据对比模块。

  1. // DiffView.js
  2. import React, { Component } from 'react';
  3. import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer';
  4. import styles from './index.less';
  5. export default class Page extends Component {
  6.   render() {
  7.     const { leftTitle, rightTitle, srcResponse, dstResponse } = this.props;
  8.     return (
  9.       <div className={styles.diffContainer}>
  10.         <ReactDiffViewer
  11.           leftTitle={leftTitle}
  12.           rightTitle={rightTitle}
  13.           oldValue={JSON.stringify(srcResponse, null2)}
  14.           newValue={JSON.stringify(dstResponse, null2)}
  15.           splitView
  16.           // showDiffOnly={false}
  17.           compareMethod={DiffMethod.JSON}
  18.           // renderContent={this.highlightSyntax}
  19.         />
  20.       </div>
  21.     )
  22.   }
  23. }

mitmproxy中的自定义dump接口

这个实际是在mitmweb模式下才会提供的一个接口,通过忽略跨域,暴露出来给前端调用。

  1. # mitmproxy/tools/web/app.py
  2. class FlowDumpByIdHandler(RequestHandler):
  3.     def get(self):
  4.         self.set_header("Content-Disposition""attachment; filename=flows")
  5.         self.set_header("Content-Type""application/octet-stream")
  6.         save_ids = self.json
  7.         bio = BytesIO()
  8.         fw = io.FlowWriter(bio)
  9.         for f in self.view:
  10.             if save_ids['saveIds']:
  11.                 if f.id in save_ids['saveIds']:
  12.                     fw.add(f)
  13.         self.write(bio.getvalue())
  14.         bio.close()

获取流量response的中间件

核心的是返回值解析部分,这里也可以不做解析就用原生的json,但是最后入库的数据量会很大,影响后续的界面展示性能,可以看到目前主要是解析了xml和json数据,应该能覆盖常见的类型了,对于后续遇到的其它形式的返回值可以自定义扩展解析。

  1. # getReplayResponse.py
  2. import mitmproxy.http
  3. from bs4 import BeautifulSoup
  4. from mitmproxy import ctx
  5. import logging, json, typing, requests, hashlib
  6. PARSE_ERROR = object()
  7. def parse_json(s: bytes) -> typing.Any:
  8.     try:
  9.         return json.loads(s.decode('utf-8'))
  10.     except ValueError:
  11.         return PARSE_ERROR
  12. class MappingAddonConfig:
  13.     HTML_PARSER = "html.parser"
  14. class GetReplayResponse:
  15.     def __init__(self):
  16.         self.serverDomain = "{appHost}"
  17.         self.taskId = "{taskId}"
  18.         self.logger = logging.getLogger(self.__class__.__name__)
  19.     def uploadToServer(self, requestId, responseData, replayRespHash):
  20.         url = self.serverDomain + '/api/replayManage/getResponseFromProxy'
  21.         data = {{
  22.             'taskId'self.taskId,
  23.             'requestId': requestId,
  24.             'responseData': responseData,
  25.             'replayRespHash': replayRespHash,
  26.         }}
  27.         headers = {{'Content-Type''application/json'}}
  28.         res = requests.post(url, data=json.dumps(data), headers=headers)
  29.         resp = res.json()
  30.         if resp['code'] != 0:
  31.             ctx.log.info("upload [ %s ] flow failed " % requestId)
  32.     def response(self, flow: mitmproxy.http.HTTPFlow) -> None:
  33.         """If a response is received, check if we should replace some content. """
  34.         try:
  35.             requestId = flow.id
  36.             res = flow.response
  37.             if res is not None:
  38.                 encoding = res.headers.get("content-encoding""utf-8")
  39.                 content_type = res.headers.get("content-type""text/html")
  40.                 replayRespHash = hashlib.sha256(res.raw_content).hexdigest()
  41.                 responseData = ''
  42.                 if "text/html" in content_type and encoding == "utf-8":
  43.                     content = BeautifulSoup(res.content, MappingAddonConfig.HTML_PARSER)
  44.                     responseData = content.encode(encoding)
  45.                 elif "json" in content_type:
  46.                     data = parse_json(res.content)
  47.                     if data is not PARSE_ERROR:
  48.                         responseData = data
  49.                     else:
  50.                         self.logger.warning(
  51.                             f"PARSE_ERROR content type '{{content_type}}'")
  52.                 else:
  53.                     self.logger.warning(f"Unsupported content type '{{content_type}}' or content encoding '{{encoding}}'")
  54.                 self.uploadToServer(requestId, responseData, replayRespHash)
  55.         except KeyError:
  56.             pass
  57. addons = [
  58.     GetReplayResponse()
  59. ]

本系统采用前后端分离架构,本文仅就 流量回放 模块的设计与实现,做分析讲解,其它模块将在后续文章中拆分讲述。

最后:下方这份完整的软件测试视频教程已经整理上传完成,需要的朋友们可以自行领取【保证100%免费】

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/599572
推荐阅读
相关标签
  

闽ICP备14008679号