实现流量回放
链接:https://zhuanlan.zhihu.com/p/334476826
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一、简介
随着应用程序的增长,测试它所需的工作也呈指数增长。GoReplay提供了一个简单的想法,可以重用现有流量进行测试,从而使其功能异常强大。先进的技术可让您分析和记录您的应用程序流量,而不会对其造成影响。这消除了将第三方组件置于关键路径中所带来的风险。
GoReplay是一个用于捕获和回放实时HTTP流量的开源工具,可以通过真实的数据不断在测试环境测试你的系统。
GoReplay提供了独特的屏蔽处理方法。后台GoReplay不再是代理,而是侦听网络接口上的流量,无需更改生产基础结构,而是在与服务相同的计算机上运行GoReplay守护程序。
GoReplay的使用增强了您对代码部署,配置和基础架构更改后的信心。
二、准备
2.1 准备工作
确认GO环境安装成功
$go version
go version go1.13.5 darwin/amd64
三、本地Demo演练
3.1 编写一个基于python的http接口服务
3.1.1 创建一个python3的虚拟环境
kenwu@KenMBP-2 ~/Documents/pythonsyscode/gortest python3 -m venv venv
kenwu@KenMBP-2 ~/Documents/pythonsyscode/gortest ll
total 0
drwxr-xr-x 6 kenwu staff 192B 12 18 20:56 venv
kenwu@KenMBP-2 ~/Documents/pythonsyscode/gortest source venv/bin/activate
(venv) kenwu@KenMBP-2 ~/Documents/pythonsyscode/gortest ll
total 0
drwxr-xr-x 6 kenwu staff 192B 12 18 20:56 venv
3.1.2 创建一个基于flask的工程
(venv) kenwu@KenMBP-2 ~/Documents/pythonsyscode/gortest pip install flask
(venv) kenwu@KenMBP-2 ~/Documents/pythonsyscode/gortest pip install flask-restful
app.py:
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '呵呵!'},
}
class TodoList(Resource):
def get(self):
return TODOS
api.add_resource(TodoList, '/todos')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8000)
启动运行flask服务:
3.2 MAC下安装Goreplay
从https://github.com/buger/gor/releases下载最新的 Gor 二进制文件(我们为 Windows、Linux x64 和 Mac OS 提供预编译的二进制文件),或者您可以自行编译编译https://github.com/buger/goreplay/wiki/Compilation。
这里我们下载Mac版本:https://github.com/buger/goreplay/releases/download/v1.0.0/gor_1.0.0_mac.tar.gz
3.2.1 解压并捕获流量通过终端输出
sudo ./gor --input-raw :8000 --output-stdout
此命令表示侦听端口 8000 上发生的所有网络活动并将其记录到粗壮。如果您熟悉 tcpdump,我们将实现类似的功能。
通过在浏览器中打开http://localhost:8000/todos,或者只需在终端调用 curl http://localhost:8000/todos,发送一些请求。您应该看到gor将所有 HTTP请求直接输出到运行它的终端窗口。请注意,默认情况下GoReplay不跟踪响应,您可以通过使用--output-http-track-response 选项启用。
kenwu@KenMBP-2 ~/Documents/tooltest pwd
/Users/kenwu/Documents/tooltest
kenwu@KenMBP-2 ~/Documents/tooltest tar xvf gor_1.0.0_mac.tar-2.gz
x gor
kenwu@KenMBP-2 ~/Documents/tooltest ll -a
total 48728
drwxr-xr-x 4 kenwu staff 128B 12 18 21:34 .
drwx------+ 22 kenwu staff 704B 12 18 21:32 ..
-rwxr-xr-x@ 1 kenwu staff 16M 3 30 2019 gor
-rw-r--r--@ 1 kenwu staff 7.1M 12 18 20:59 gor_1.0.0_mac.tar-2.gz
kenwu@KenMBP-2 ~/Documents/tooltest sudo ./gor --input-raw :8000 --output-stdout
Password:
Version: 1.0.0
1 dad10dcf728fd1f9cc857a8f5e0d44c91b4db455 1576676146990796000
GET /todos HTTP/1.1
Host: localhost:8000
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Sec-Fetch-User: ?1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
连续两次请求:http://localhost:8000/todos
3.2.2 回放到其他服务器
现在是时候将原始流量重播到另一个环境了。让我们启动同一个工程服务,但在不同的端口:http://localhost:8001/todos。代替 -outpue-stdout 我们将使用 --output-http 并提供第二个服务器的 URL:sudo ./gor --intput-raw :8000 --output-http="http://localhost:8001"。向第一个服务器发出很少的请求。你应该看到他们复制到第二个。
kenwu@KenMBP-2 ~/Documents/tooltest sudo ./gor --input-raw :8000 --output-http="http://localhost:8001/todos"
Password:
Version: 1.0.0
3.2.3 保存请求到文件,使用文件回放
有的时候,不可能实时回放请求,Gor允许我们保存请求到文件,并且使用它进行回放。
首先使用:sudo ./gor --input-raw :8000 --output-file=requests.gor
这将创建新文件requests_0.gor,并持续写入所有捕获的请求。
kenwu@KenMBP-2 ~/Documents/tooltest sudo ./gor --input-raw :8000 --output-file=requests.gor
Version: 1.0.0
使用请求文件,请求8001服务器:
kenwu@KenMBP-2 ~/Documents/tooltest ./gor --input-file requests_0.gor --output-http="http://localhost:8001"
Version: 1.0.0
2019/12/18 22:23:53 FileInput: end of file 'requests_0.gor'
好了,我们对gor有了个初步的认识,
其它基本用法:
简单的 HTTP 流量复制:
gor –input-raw :80 –output-http “http://staging.com”
HTTP 流量复制频率控制:
gor –input-tcp :28020 –output-http “http://staging.com|10″
HTTP 流量复制缩小:
gor –input-raw :80 –output-tcp “replay.local:28020|10%”
HTTP 流量记录到本地文件:
gor –input-raw :80 –output-file requests.gor
HTTP 流量回放和压测:
gor –input-file “requests.gor|200%” –output-http “staging.com”
HTTP 流量过滤复制:
gor –input-raw :8080 –output-http staging.com –output-http-url-regexp ^www
参数:
-cpuprofile string
write cpu profile to file
-debug verbose
打开debug模式,显示所有接口的流量
-http-allow-header value
用一个正则表达式来匹配http头部,如果请求的头部没有匹配上,则被拒绝
gor --input-raw :8080 --output-http staging.com --http-allow-header api-version:^v1 (default [])
-http-allow-method value
类似于一个白名单机制来允许通过的http请求方法,除此之外的方法都被拒绝.
gor --input-raw :8080 --output-http staging.com --http-allow-method GET --http-allow-method OPTIONS (default [])
-http-allow-url value
一个正则表达式用来匹配url, 用来过滤完全匹配的的url,在此之外的都被过滤掉
gor --input-raw :8080 --output-http staging.com --http-allow-url ^www. (default [])
-http-disallow-header value
用一个正则表达式来匹配http头部,匹配到的请求会被拒绝掉
gor --input-raw :8080 --output-http staging.com --http-disallow-header "User-Agent: Replayed by Gor" (default [])
-http-disallow-url value
用一个正则表达式来匹配url,如果请求匹配上了,则会被拒绝
gor --input-raw :8080 --output-http staging.com --http-disallow-url ^www. (default [])
-http-header-limiter value
读取请求,基于FNV32-1A散列来拒绝一定比例的特殊请求
gor --input-raw :8080 --output-http staging.com --http-header-imiter user-id:25% (default [])
-http-original-host
在--output-http的输出中,通常gor会使用取代请求的http头,所以应该禁用该选项,保留原始的主机头
-http-param-limiter value
Takes a fraction of requests, consistently taking or rejecting a request based on the FNV32-1A hash of a specific GET param:
gor --input-raw :8080 --output-http staging.com --http-param-limiter user_id:25% (default [])
-http-rewrite-url value
Rewrite the request url based on a mapping:
gor --input-raw :8080 --output-http staging.com --http-rewrite-url /v1/user/([^\/]+)/ping:/v2/user/$1/ping (default [])
-http-set-header value
Inject additional headers to http reqest:
gor --input-raw :8080 --output-http staging.com --http-set-header 'User-Agent: Gor' (default [])
-http-set-param value
Set request url param, if param already exists it will be overwritten:
gor --input-raw :8080 --output-http staging.com --http-set-param api_key=1 (default [])
-input-dummy value
Used for testing outputs. Emits 'Get /' request every 1s (default [])
-input-file value
从一个文件中读取请求
gor --input-file ./requests.gor --output-http staging.com (default [])
-input-http value
从一个http接口读取请求
# Listen for http on 9000
gor --input-http :9000 --output-http staging.com (default [])
-input-raw value
Capture traffic from given port (use RAW sockets and require *sudo* access):
# Capture traffic from 8080 port
gor --input-raw :8080 --output-http staging.com (default [])
-input-tcp value
用来在多个gor之间流转流量
# Receive requests from other Gor instances on 28020 port, and redirect output to staging
gor --input-tcp :28020 --output-http staging.com (default [])
-memprofile string
write memory profile to this file
-middleware string
Used for modifying traffic using external command
-output-dummy value
用来测试输入,打印出接收的数据. (default [])
-output-file value
把进入的请求写入一个文件中
gor --input-raw :80 --output-file ./requests.gor (default [])
-output-http value
转发进入的请求到一个http地址上
# Redirect all incoming requests to staging.com address
gor --input-raw :80 --output-http http://staging.com (default [])
-output-http-elasticsearch string
把请求和响应状态发送到ElasticSearch:
gor --input-raw :8080 --output-http staging.com --output-http-elasticsearch 'es_host:api_port/index_name'
-output-http-redirects int
设置多少次重定向被允许
-output-http-stats
每5秒钟输出一次输出队列的状态
-output-http-timeout duration
指定http的request/response超时时间,默认是5秒
-output-http-workers int
gor默认是动态的扩展工作者数量,你也可以指定固定数量的工作者
-output-tcp value
用来在多个gor之间流转流量
# Listen for requests on 80 port and forward them to other Gor instance on 28020 port
gor --input-raw :80 --output-tcp replay.local:28020 (default [])
-output-tcp-stats
每5秒钟报告一次tcp输出队列的状态
-split-output true
By default each output gets same traffic. If set to true it splits traffic equally among all outputs.
-stats
打开输出队列的状态
个人感觉,这工具看上去很美,其实局限性还是很大的。
如果有token, 转到其他环境,就难办了。
然后只能获取get的请求,如果需要post改数据,就难办了。你得在相应得环境中有同样得数据。
办法也不是没有的,你需要中间件。
中间件可以帮助我们实现对数据的复杂操作,goreplay官方描述其支持多种语言的中间件开发,并且给了nodejs的官方库,这里我以python版本的api库为例进行中间件开发。
pip install gor
使用库
实现的功能,把监听serverA返回的responose和镜像serverB的response进行标识,并发送给后端服务,以便做数据对比以及记录跟踪。
middleware.py
import sys,requests,json,datetime
from gor.middleware import TornadoGor
def log(msg):
"""
Logging to STDERR as STDOUT and STDIN used for data transfer
@type msg: str or byte string
@param msg: Message to log to STDERR
"""
try:
msg = str(msg) + '\n'
except:
pass
sys.stderr.write(msg)
sys.stderr.flush()
def sendResponse(fromType,id,postData):
headers = {
'Content-Type': 'application/json'
}
data = {
'fromType': fromType,
'id': id,
'postData': postData
}
res = requests.post('http://127.0.0.1:5000/getResponse',data=json.dumps(data),headers=headers)
response = res.json()
logTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log("[{}]: fromType={},id={},{}".format(logTime,fromType,id,response['msg']))
def on_request(proxy, msg, **kwargs):
sendResponse(1, msg.id, msg.http)
proxy.on('response', on_response, idx=msg.id, req=msg)
def on_response(proxy, msg, **kwargs):
sendResponse(2,msg.id,msg.http.split('\r\n\r\n')[1])
proxy.on('replay', on_replay, idx=kwargs['req'].id, req=kwargs['req'], resp=msg)
def on_replay(proxy, msg, **kwargs):
# proxy.set_http_header(msg.http, 'tttttttttt', '88888888888888')
sendResponse(3, msg.id, msg.http.split('\r\n\r\n')[1])
if __name__ == '__main__':
proxy = TornadoGor()
proxy.on('request', on_request)
proxy.run()
这里我是先在服务端的nginx里加了个配置,反向代理一个端口到serverA,并用goreplay监听这个端口,来实现的区分数据来源。
sudo ./gor --input-raw :serverAPort --output-http "http://serverB" --input-raw-track-respons--output-http-track-response --prettify-http --middleware "python3 middleware.py"
这样就能绕过一些坑。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)