1、goreplay流量回放

  • 目的

在实际项目中,会有大量的回归测试工作,通常会使用自动化代码的手段来实现回归,但是对于一个庞大的系统来说,通过自动化脚本的方式来实现回归测试,又显得很费时费力。并且如果有定期将线上数据同步到测试环境的需求的话,流量回放就显得很必要了。当然,流量回放并不能替代自动化,对于存在上下文关联的接口,流量回放的处理上就显得很复杂。

  • 流量回放

其实流量回放主要是对某个端口的流量进行转发和存储,这边用的是goreplay,mitmproxy也可以做到流量的抓取和回放。

流量回放有三种使用方式:

  • 服务->to->服务,即端到端
  • 服务->to->文件,即流量存储
  • 文件->to->服务,即流量文件回放到服务
  • 常用配置示例
  • service_to_file.yml
bin:
  - "/opt/crawl/project/flow_replay/gor"
input:
  - "--input-raw :18080" 
  - "--input-raw-track-response"
  - "--prettify-http"
middle:
  - 
output:
  - "--output-file ./data/requests-%Y-%m-%d-%H.gor"
  - "--output-file-size-limit 100kb"
  - "--output-file-queue-limit 60"
  • file_to_service.yml
bin:
  - "/opt/crawl/project/flow_replay/gor"
input:
  - "--input-file ./data/'requests-2024-03-18-14_7.gor'"
middle:
  - "--middleware /opt/crawl/project/flow_replay/middle/main.js"
output:
  - "--output-http='http://localhost:18080'"
  - "--output-http-track-response"
  - "--prettify-http"
  • 释义

gorplay的配置主要分为三个部分,分别是input、output和middleware

  • input:定义需要捕获请求的一些必要条件
# 定义监听端口
--input-raw :18080 
# 定义是否要捕获请求响应,只有添加了这个配置,回放时才会触发response事件
--input-raw-track-response
# 定义是否要美化响应,这个配置是作者修复bug新加的配置,必须要有,
# 如果没有会造成乱码
--prettify-http
# 定义存储请求的文件位置
# 可以使用*模糊匹配,批量回放相关文件,如requests-2024-03-18-*.gor
--input-file ./data/'requests-2024-03-18-14_7.gor'
# 定义缓冲区大小,单位为b
--input-raw-buffer-size 10485760

注:不论是从端口读取请求或者从文件读取请求,则必然会触发request事件

  • output:定义输出请求的一些必要参数
# 定义存储请求文件的位置、格式及命名
--output-file ./data/requests-%Y-%m-%d-%H.gor
# 追踪流量回放响应,只有存在这个配置,回放成功后才会触发replay事件
--output-http-track-response
# 定义存储请求文件的最大size
--output-file-size-limit 100kb
# 定义单个存储文件中存储请求的数量
--output-file-queue-limit 20
# 同input
--prettify-http
# 定义请求丢弃比例,分别为绝对值和百分比
# 每秒请求超过绝对值的数字,则丢弃
# 只回放百分比的请求数量提升速度
--output-http='http://localhost:18080|10'
--output-http='http://localhost:18080|10%'
  • 中间件
  • 中间件流程示意图

  • 中间件常用Api
init - 初始化
httpPath - 获取请求URL,不包含请求的域名: gor.httpPath(req.http)
httpMethod - 获取请求方法:gor.httpMethod(req.http).
setHttpPath - 更新URL: req.http = gor.setHttpPath(req.http, newPath)
httpPathParam - 获取URL上的参数: gor.httpPathParam(req.http, queryParam)
setHttpPathParam - 设置URL参数: req.http = gor.setHttpPathParam(req.http, queryParam, value)
httpStatus - 响应状态码:gor.httpStatus(rep.http)
httpHeaders - 获取所有请求头: gor.httpHeaders(req.http)
httpHeader - 获取指定请求头: gor.httpHeader(req.http, "Content-Length")
setHttpHeader - 设置请求头, returns modified payload: req.http = gor.setHttpHeader(req.http, "X-Replayed", "1")
httpBody - 获取请求体: gor.httpBody(req.http)
setHttpBody - 设置请求体,需要注意Content-Length的大小: req.http = gor.setHttpBody(req.http, Buffer.from('hello!')).
httpBodyParam - 获取post请求param: gor.httpBodyParam(req.http, param)
setHttpBodyParam - 设置post请求的param: req.http = gor.setHttpBodyParam(req.http, param, value)
httpCookie - 获取cookie: gor.httpCookie(req.http, "SESSSION_ID")
setHttpCookie - 设置cookie, returns modified payload: req.http = gor.setHttpCookie(req.http, "iam", "cuckoo")
deleteHttpCookie - 删除cookie, returns modified payload: req.http = gor.deleteHttpCookie(req.http, "iam")
  • 事件

事件只存在于中间件中,goreplay接收到某个端口的请求后,会把request和response压缩成gzip,通过data.http字段转发给中间件,这样会触发request事件和response事件,当回放完成后,会触发replay事件

  • 中间件代码示例
#!/usr/bin/node
const gor = require("goreplay_middleware");
const NodeRSA = require('node-rsa');
const axios = require('axios')
const {user, baseUrl, publicKey} = require('./defalut')
const https = require('https');
const jsonpath = require('jsonpath');

/**
 * @description 对公钥进行加密
 * @param publicKey
 * @param {string} data 需要加密的数据
 * @returns {string} 加密后的数据
 */
function encrypt(publicKey, data) {
    let buffer = Buffer.from(data);
    let key = new NodeRSA(publicKey);
    key.setOptions({encryptionScheme: 'pkcs1'});
    return key.encrypt(buffer, 'base64', 'base64');
}


/**
 *
 * @param str
 * @param color:string, red, green, yellow, blue, and white if the color is the other value
 * @return {string}
 */
function addStringColor(str, color) {
    switch (color) {
        case "red":
            color = "31";
            break;
        case "green":
            color = "32";
            break;
        case "yellow":
            color = "33";
            break;
        case "blue":
            color = "34";
            break;
        case "pink":
            color = "35";
            break;
        default:
            color = "37";
            break;
    }
    return "\x1b[" + color + "m" + str + "\x1b[0m";
}


/**
 * @description send login request and extract sessionId
 * @param baseUrl{string} request url
 * @returns {string} return sessionId
 */

async function getCookie(baseUrl) {
    const data = {
        userName: user["username"],
        password: encrypt(publicKey, user["password"])
    };
    const url = baseUrl.concat('api/auth/doLogin');
    try {
        const response = await axios.post(url, data, {
            httpsAgent: new https.Agent({rejectUnauthorized: false}),
            headers: {'Content-Type': 'application/json; charset=utf-8'}
        });
        return response.headers['set-cookie'][0].match('(?<=JSESSIONID=)[a-f0-9-]+')[0];
    } catch (error) {
        console.error(error);
    }
}

(async () => {
    let cookie = await getCookie(baseUrl);
    gor.init();
    gor.on('request', function (request) {
        //request.http = gor.deleteHttpCookie(request.http, 'JSESSIONID')
        request.http = gor.setHttpCookie(request.http, 'JSESSIONID', cookie)
        gor.on("replay", request.ID, function (replay) {
            try {
                let obj = JSON.parse(gor.httpBody(replay.http).toString('utf8'))
                if (jsonpath.query(obj, '$..code')[0] === '0000') {
                    console.error(addStringColor(`√\t${gor.httpMethod(request.http)}\t${gor.httpPath(request.http)}`, 'green'))
                } else {
                    console.error(addStringColor(`x\t${gor.httpMethod(request.http)}\t${gor.httpPath(request.http)}\n ${gor.httpBody(replay.http)} `, 'red'))
                }
            } catch (e) {
                console.error(addStringColor(`\t${gor.httpMethod(request.http)}\t${gor.httpPath(request.http)}\n ${e}`, 'red'))
            }
            return replay;
        })
        return request
    })
})()
  • 效果展示

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
流量回放工具goreplay是一个开源的工具,它可以用来记录和回放HTTP/HTTPS流量。如果你想要进行压测并统计结果,可以对goreplay进行一些改造。 首先,你需要安装goreplay并启动它来进行流量录制和回放。然后,你可以使用一个脚本来发送请求并记录响应时间。以下是一个示例脚本: ```bash #!/bin/bash while read line; do url=$(echo $line | cut -d ' ' -f 2) time=$(curl -o /dev/null -s -w %{time_total} $url) echo $time >> response_times.log done < urls.txt ``` 这个脚本从一个名为urls.txt的文件中读取URL,并使用curl发送请求并记录响应时间。响应时间被写入response_times.log文件中。 一旦你有了响应时间的记录,你可以使用其他工具来进行统计和分析。例如,你可以使用awk命令来计算平均响应时间: ```bash awk '{sum+=$1} END {print "Average response time: " sum/NR "s"}' response_times.log ``` 这个命令将response_times.log文件中的所有响应时间相加,然后除以记录数来计算平均响应时间。 你也可以使用其他工具,如Grafana、Prometheus和InfluxDB等来可视化和分析数据。例如,你可以使用Grafana来创建一个响应时间的实时监控面板。 改造goreplay支持压测统计是可行的,但需要一定的技术水平和时间投入。你需要了解golang和HTTP/HTTPS协议,并对goreplay的代码进行修改。如果你需要更加专业的支持,可以考虑向goreplay的开发团队寻求帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值