流量回放工具之 GoReplay 安装及使用

一、Goreplay 介绍

GoReplay 是一款强大的开源网络监控工具。它能助力研究人员捕捉、监控和记录实时 HTTP 流量,还可将这些流量重放到测试环境,利用真实数据分析系统的数据连贯性。此外,该工具对提升代码部署、配置修改和基础架构的数据完整性也有帮助。

值得注意的是,GoReplay 运用先进技术,能在不影响应用程序流量的前提下,对其进行分析和记录,避免了在业务关键路径中引入第三方组件带来的风险。

GoReplay 并非代理程序,它在后台监听网络接口的流量,无需对生产基础架构做更改,只需在机器上运行 GoReplay 守护进程即可。

开源地址: github.com/buger/gorep…

相较于 TCPCopy,GoReplay 的架构更为简单,仅包含一个 gor 组件,如下所示:

TCPCopy架构

GoReplay架构

只需在生产服务器上启动一个 gor 进程,它就能承担起监听、过滤和转发等所有工作。

GoReplay 的设计遵循 Unix 设计哲学: 一切皆由管道构成,各类输入数据均可复用为输出。

二、Golang环境安装

一 、首先安装Golang及相关依赖环境

在官网上下载安装包或者go的一些中文网站上下载安装包

  1. golang.org/dl/
  2. studygolang.com/dl

我下载的是: go1.15.5.linux-amd64.tar.gz

二、解压到/usr/local目录下

tar -C /usr/local -zxvf go1.14.4.linux-amd64.tar.gz

三、配置环境变量

# 打开
vim /etc/profile

# 添加
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin

# 编译生效
source /etc/profile

四、验证

go env

[root@vm ~]# go version
go version go1.15.5 linux/amd64

三、Goreplay 安装

github.com/buger/gor/r… 下载最新的 Gor 二进制文件(提供 Windows、Linux x64 和 Mac OS 的预编译二进制文件),也可以自己 编译

在这里插入图片描述

下载二进制包:

[root@vm-1 ~]# curl -L -O https://github.com/buger/goreplay/releases/download/v1.3.1/gor_1.3.1_x64.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   626  100   626    0     0    741      0 --:--:-- --:--:-- --:--:--   741
100 10.5M  100 10.5M    0     0  3258k      0  0:00:03  0:00:03 --:--:-- 5951k

[root@vm-1 ~]# tar xvzf gor_1.3.1_x64.tar.gz
gor

解压缩包后,您可以从当前目录运行 Gor,或者您可能希望将二进制文件复制到您的 PATH(对于 Linux 和 Mac OS,它可以是 /usr/local/bin)。

四、Goreplay 使用示例

goreplay流量回放示意图

1、准备 RESTful API 环境

接口设计:

请求类型 请求路径 功能
Get /person 查询所有人
Post /person/two 事务测试

打包程序分别放到两台服务器 run 起来。

服务器一:

[root@vm-1 ~]# java -jar spirng-boot-demo-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.8.RELEASE)

2021-08-17 14:19:10.467  WARN 17537 --- [           main] o.s.boot.StartupInfoLogger               : InetAddress.getLocalHost().getHostName() took 10014 milliseconds to respond. Please verify your network configuration.
2021-08-17 14:19:20.494  INFO 17537 --- [           main] com.zuozewei.SpirngbootdemoApplication   : Starting SpirngbootdemoApplication v0.0.1-SNAPSHOT on vm-jmeter with PID 17537 (/root/spirng-boot-demo-0.0.1-SNAPSHOT.jar started by root in /root)
2021-08-17 14:19:20.495  INFO 17537 --- [           main] com.zuozewei.SpirngbootdemoApplication   : The following profiles are active: a
2021-08-17 14:19:22.225  INFO 17537 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-08-17 14:19:22.355  INFO 17537 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 111ms. Found 1 JPA repository interfaces.
2021-08-17 14:19:23.642  INFO 17537 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8082 (http)
2021-08-17 14:19:23.667  INFO 17537 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-08-17 14:19:23.667  INFO 17537 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-08-17 14:19:23.804  INFO 17537 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-08-17 14:19:23.805  INFO 17537 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3179 ms
2021-08-17 14:19:24.314  INFO 17537 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-08-17 14:19:24.479  INFO 17537 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.27.Final
2021-08-17 14:19:25.002  INFO 17537 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-08-17 14:19:25.228  INFO 17537 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-08-17 14:19:25.635  INFO 17537 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-08-17 14:19:25.706  INFO 17537 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL57Dialect
2021-08-17 14:19:27.086  INFO 17537 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-08-17 14:19:27.112  INFO 17537 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-08-17 14:19:27.990  WARN 17537 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2021-08-17 14:19:28.261  INFO 17537 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-08-17 14:19:28.749  INFO 17537 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8082 (http) with context path ''
2021-08-17 14:19:28.770  INFO 17537 --- [           main] com.zuozewei.SpirngbootdemoApplication   : Started SpirngbootdemoApplication in 39.553 seconds (JVM running for 40.431)

服务器二:

[root@vm-2 ~]# java -jar spirng-boot-demo-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.8.RELEASE)

2021-08-17 14:18:34.814  INFO 12775 --- [           main] com.zuozewei.SpirngbootdemoApplication   : Starting SpirngbootdemoApplication v0.0.1-SNAPSHOT on vm-hsyjy-znkzb-vbi-1.novalocal with PID 12775 (/root/spirng-boot-demo-0.0.1-SNAPSHOT.jar started by root in /root)
2021-08-17 14:18:34.820  INFO 12775 --- [           main] com.zuozewei.SpirngbootdemoApplication   : The following profiles are active: a
2021-08-17 14:18:36.293  INFO 12775 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2021-08-17 14:18:36.419  INFO 12775 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 107ms. Found 1 JPA repository interfaces.
2021-08-17 14:18:37.694  INFO 12775 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8082 (http)
2021-08-17 14:18:37.716  INFO 12775 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-08-17 14:18:37.717  INFO 12775 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-08-17 14:18:37.841  INFO 12775 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-08-17 14:18:37.841  INFO 12775 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2886 ms
2021-08-17 14:18:38.305  INFO 12775 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2021-08-17 14:18:38.444  INFO 12775 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.27.Final
2021-08-17 14:18:38.864  INFO 12775 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
2021-08-17 14:18:39.119  INFO 12775 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-08-17 14:18:39.556  INFO 12775 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2021-08-17 14:18:39.648  INFO 12775 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL57Dialect
2021-08-17 14:18:41.052  INFO 12775 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2021-08-17 14:18:41.086  INFO 12775 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2021-08-17 14:18:42.042  WARN 12775 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2021-08-17 14:18:42.330  INFO 12775 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-08-17 14:18:42.768  INFO 12775 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8082 (http) with context path ''
2021-08-17 14:18:42.786  INFO 12775 --- [           main] com.zuozewei.SpirngbootdemoApplication   : Started SpirngbootdemoApplication in 8.988 seconds (JVM running for 9.939)

2、捕获服务器一流量保存到本地文件

在终端运行这个命令:

#将端口 8082 流量保存到本地的文件
sudo ./gor --input-raw :8082 --output-file=requests.gor

[root@vm-1 ~]# sudo ./gor --input-raw :8082 --output-file=requests.gor
Interface: eth0 . BPF Filter: ((tcp dst port 8082) and (dst host 172.16.106.149 or dst host fe80::f599:6e65:3dc9:b84a))
Interface: lo . BPF Filter: ((tcp dst port 8082) and (dst host 127.0.0.1 or dst host ::1))
Interface: virbr0 . BPF Filter: ((tcp dst port 8082) and (dst host 192.168.124.1))
2021/08/17 14:26:41 [PPID 17654 and PID 17655] Version:1.3.0

注意:

使用–output-file-append 选项可以将捕获流量保存为一个单独的文件 使用sudo并要求输入密码:为了分析网络,Gor 需要只有超级用户才能使用的权限。但是,可以 为非 root 用户 配置 运行 Gor 。

在 Postman 上向服务器一发送请求:

在这里插入图片描述

我们查看服务一日志:

Hibernate: select person0_.id as id1_0_, person0_.age as age2_0_, person0_.name as name3_0_ from person person0_
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into person (age, name, id) values (?, ?, ?)
Hibernate: insert into person (age, name, id) values (?, ?, ?)

可以看到服务已经正常处理了。

我们停掉在服务器一上运行的 Gor,可以看到已经生成了录制的请求文件:

[root@vm-1 ~]# ls -l | grep "requests"
-rw-r----- 1 root    root         300 8月  17 14:33 requests_0.gor

我们看下请求文件的内容:

[root@vm-1 ~]# cat requests_0.gor
1 04481f92ac106afb42ce55ca 1629182282437724065 0
GET /person HTTP/1.1
User-Agent: PostmanRuntime/7.28.2
Accept: */*
Cache-Control: no-cache
Postman-Token: ab405d20-ef93-4ffb-b2f6-a16cf1205e7c
Host: 172.16.106.149:8082
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

3、将流量从文件回放到服务器二

现在是时候将原始流量回放到服务二中了。如果是多个请求并且它们将按照与记录的顺序和时间完全相同的顺序重放。

–output-http 提供第二个服务的 URL

./gor --input-file requests_0.gor --output-http="http://172.16.106.237:8082"

[root@vm-1 ~]./gor --input-file requests_0.gor --output-http="http://172.16.106.237:8082"
2021/08/17 14:42:55 [PPID 12356 and PID 17914] Version:1.3.0
[DEBUG][elapsed 985.792µs]: [INPUT-FILE] FileInput: end of file 'requests_0.gor'

我们看到服务二的日志:

[root@vm-2 ~]# java -jar spirng-boot-demo-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.8.RELEASE)

2021-08-17 14:18:34.814  INFO 12775 --- [           main] com.zuozewei.SpirngbootdemoApplication   : Starting SpirngbootdemoApplication v0.0.1-SNAPSHOT on vm-hsyjy-znkzb-vbi-1.novalocal with PID 12775 (/root/spirng-boot-demo-0.0.1-SNAPSHOT.jar started by root in /root)
......

Hibernate: select person0_.id as id1_0_, person0_.age as age2_0_, person0_.name as name3_0_ from person person0_

日志说明服务正常响应。

4、实时将流量请求打印到终端

通过命令:

sudo ./gor --input-raw :8082 --output-stdout

此命令其记录到标准控制台输出,请注意,默认情况下 GoReplay 不跟踪响应,您可以使用 –output-http-track-response 选项启用它们。

[root@vm-1 ~]sudo ./gor --input-raw :8082 --output-stdout
Interface: eth0 . BPF Filter: ((tcp dst port 8082) and (dst host 172.16.106.149 or dst host fe80::f599:6e65:3dc9:b84a))
Interface: lo . BPF Filter: ((tcp dst port 8082) and (dst host 127.0.0.1 or dst host ::1))
Interface: virbr0 . BPF Filter: ((tcp dst port 8082) and (dst host 192.168.124.1))
2021/08/17 14:55:45 [PPID 18060 and PID 18061] Version:1.3.0
1 22ee1f92ac106afb594d205a 1629183362175214036 0
POST /person/two HTTP/1.1
User-Agent: PostmanRuntime/7.28.2
Accept: */*
Cache-Control: no-cache
Postman-Token: 4922ce23-5706-42c5-8a54-ade5d5aee785
Host: 172.16.106.149:8082
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 0

5、复制流量实时转发到服务器二

运行命令:

sudo ./gor --input-raw :8082 --output-http="http://172.16.106.237:8082"

[root@vm-1 ~]# sudo ./gor --input-raw :8082 --output-http="http://172.16.106.237:8082"
Interface: eth0 . BPF Filter: ((tcp dst port 8082) and (dst host 172.16.106.149 or dst host fe80::f599:6e65:3dc9:b84a))
Interface: lo . BPF Filter: ((tcp dst port 8082) and (dst host 127.0.0.1 or dst host ::1))
Interface: virbr0 . BPF Filter: ((tcp dst port 8082) and (dst host 192.168.124.1))
2021/08/17 14:59:49 [PPID 18118 and PID 18119] Version:1.3.0

在服务器二查看日志,.发现流量已经进来了:

Hibernate: select person0_.id as id1_0_, person0_.age as age2_0_, person0_.name as name3_0_ from person person0_
Hibernate: select person0_.id as id1_0_, person0_.age as age2_0_, person0_.name as name3_0_ from person person0_
Hibernate: select person0_.id as id1_0_, person0_.age as age2_0_, person0_.name as name3_0_ from person person0_ where person0_.age=?

6、压力测试

goreplay支持将捕获到的生产实际请求流量减少或者放大重播以用于测试环境的压力测试.压力测试一般针对 Input 流量减少或者放大。

录制的请求文件:

[root@vm-1 ~]# cat requests.gor
1 040f1f92ac106afb7e8d5679 1629183084044026882 0
POST /person/two HTTP/1.1
User-Agent: PostmanRuntime/7.28.2
Accept: */*
Cache-Control: no-cache
Postman-Token: a8600dd1-28c1-4825-b6a2-68c42659942f
Host: 172.16.106.149:8082
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 0


[root@vm-jmeter ~]# cat requests.gor
1 040f1f92ac106afb7e8d5679 1629183084044026882 0
POST /person/two HTTP/1.1
User-Agent: PostmanRuntime/7.28.2
Accept: */*
Cache-Control: no-cache
Postman-Token: a8600dd1-28c1-4825-b6a2-68c42659942f
Host: 172.16.106.149:8082
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 0

我们可以看到主要是两个请求。

运行以下命令,将流量从文件回放到服务器二,并放大两倍:

[root@vm-1 ~]./gor --input-file "requests.gor|200%" --output-http="http://172.16.106.237:8082"
2021/08/17 15:03:58 [PPID 12356 and PID 18187] Version:1.3.0
[DEBUG][elapsed 1.361742ms]: [INPUT-FILE] FileInput: end of file 'requests.gor'

我们查看下服务二日志的处理情况:

Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: select next_val as id_val from hibernate_sequence for update
Hibernate: update hibernate_sequence set next_val= ? where next_val=?
Hibernate: insert into person (age, name, id) values (?, ?, ?)
Hibernate: insert into person (age, name, id) values (?, ?, ?)

我们可以看到服务处理了四个请求。

注意,当然也支持请求流量 10%,20% 等缩小。

五、Goreplay 命令解析

Goreplay 常用参数说明

  • –input-raw:用于捕捉 HTTP 流量,此参数需要指定 IP 地址与端口。
  • –input-file:负责接收流量。
  • –output-file:指定用于保存流量的文件。
  • –input-tcp:可将多个 Goreplay 实例所获取的流量汇聚到一个 Goreplay 实例中。
  • –output-stdout:将结果输出至终端。
  • –output-tcp:能够把获取到的流量转移至另一个 Goreplay 实例。
  • –output-http:指定流量释放的目标服务器,使用时需指定 IP 地址和端口。
  • –http-disallow-url:不允许正则匹配的 URL 通过。
  • –http-allow-header:设置允许的请求头(Header)。
  • –http-disallow-header:设置不允许的请求头(Header)。
  • –http-allow-method:规定允许的请求方法,可传入的值如 GET、POST、OPTIONS 等。
  • –input-file-loop:开启无限循环模式,避免读取完文件后就停止。
  • –output-http-workers:设置并发请求的数量。
  • –stats --out-http-stats:每 5 秒输出一次 TPS 数据,方便查看统计信息。
  • –split-output true:采用轮询方式对流量进行分割。
  • –output-http-timeout 30s:设置 HTTP 请求的超时时间为 30 秒,默认超时时间是 5 秒。

命令行使用

捕捉流量并通过终端输出

sudo ./goreplay --input-raw :8000 --output-stdout

上述命令会对 8000 端口上的所有流量进行监控,并将其通过终端的标准输出(stdout)显示出来。你可以使用浏览器或者 curl 工具访问 8000 端口,之后就能在终端看到 gor 输出的所有 HTTP 请求信息。

捕捉流量并实时同步到另一台机器

sudo ./goreplay --input-raw :8000 --output-http="http://example:8001"

此命令会将 8000 端口的流量实时同步到 http://example:8001 服务器。当你访问第一台服务器时,会发现流量会以相同的顺序发送到第二台服务器。

将捕捉流量保存到文件中,然后释放到其他机器

首先,使用 –output-file 参数保存流量(有时候实现实时同步流量较为困难,所以 Goreplay 提供了这种先保存后释放的模式):

sudo ./goreplay --input-raw :8000 --output-file = requests.gor

上述命令会把 8000 端口的流量保存到 requests.gor 文件中(文件后缀必须为 .gor,经测试,使用其他后缀在释放流量时可能会出现问题)。

释放保存的流量

sudo ./goreplay --input-file requests.gor --output-http = "http://localhost:8001"

该命令会将 requests.gor 文件中保存的所有请求,按照相同的时间顺序释放到 http://localhost:8001 服务器。

Goreplay 的限速机制和请求过滤

限速机制

由于生产服务器的配置通常远高于测试服务器,直接将生产服务器的全部流量同步到测试服务器是不可行的。Goreplay 提供了以下两种限速策略:

  1. 限制每秒的请求数

    sudo ./goreplay  --input-tcp :28020 --output-http "http://staging.com|10" # (每秒请求数限制在 10 个以内)
    sudo ./goreplay  --input-raw :80 --output-tcp "replay.local:28020|10%"  # (每秒请求数限制在 10% 以内)
    
  2. 基于 Header 或 URL 的参数限制一些请求,为指定的 Header 或者 URL 的请求设定限制的百分比

    sudo ./goreplay  --input-raw :80 --output-tcp "replay.local:28020|10%" --http-header-limiter "X-API-KEY: 10%"
    sudo ./goreplay  --input-raw :80 --output-tcp "replay.local:28020|10%" --http-param-limiter "api_key: 10%"
    
请求过滤

当需要捕捉指定路径的请求流量时,可以使用该机制。例如,只同步 /api 路径下的请求:

sudo ./goreplay --input-raw :8080 --output-http staging.com --http-allow-url /api

示例演示

  1. 性能测试:若进行性能测试,不考虑请求的顺序和速率,且要求无限循环。

    # --input-file 从文件中获取请求数据,重放的时候以 100 倍速进行
    # --input-file-loop 开启无限循环,避免读完文件就停止
    # --output-http 发送请求到 http://host2.com
    # --output-http-workers 并发发送 100 个请求
    # --stats --output-http-stats 每 5 秒输出一次 TPS 数据
    $ ./goreplay --input-file 'request.gor|10000%' --input-file-loop --output-http 'http://host2.com' --output-http-workers 100 --stats --output-http-stats
    
  2. 抓取特定 URL 的 HTTP 请求并输出到终端:抓取 80 端口的 HTTP 请求,仅抓取 URL 为 /api/v1 的请求,并将其输出到终端。

    $ ./goreplay --input-raw :80 --http-allow-url '/api/v1' --output-stdout
    
  3. 抓取请求并保存到文件:抓取 80 端口的所有请求,并保存到文件,实际会分批保存为 request_0.gorrequest_1.gor 这类文件名。

    $ ./goreplay --input-raw :80 --output-file 'request.gor'
    
  4. 流量回放到多个站点(复制引流)

    sudo ./gor --input-tcp :28020 --output-http "http://staging.com"  --output-http "http://dev.com"
    
  5. 按照轮询方式分割流量(平分流量)

    sudo ./gor --input-raw :80 --output-http "http://staging.com"  --output-http "http://dev.com" --split-output true
    
  6. HTTP 超时设置

    gor --input-tcp replay.local:28020 --output-http http://staging.com --output-http-timeout 30s
    
  7. 性能测试(放大回放速度):表示以 2 倍速度来回放流量。

    gor --input-file "requests.gor|200%" --output-http "staging.com"
    
  8. 限制回放速率(绝对值):回放速率不超过 10 个请求每秒(QPS)。

    gor --input-tcp :28020 --output-http "http://staging.com|10"
    
  9. 限制回放速率(百分比):回放流量不超过原流量的 10%(这里是总流量的占比)。

    gor --input-raw :80 --output-tcp "replay.local:28020|10%"
    
  10. 禁止特定 URL 正则匹配的请求:禁止除 /api 之外的请求。

    gor --input-raw :8080 --output-http staging.com --http-disallow-url /api
    
  11. 基于请求方法过滤:只允许 GET 和 OPTIONS 请求。

    gor --input-raw :80 --output-http "http://staging.server" --http-allow-method GET --http-allow-method OPTIONS
    
  12. 基于请求头过滤

    gor --input-raw :8080 --output-http staging.com --http-allow-header api-version:^1\.0\d
    gor --input-raw :8080 --output-http staging.com --http-disallow-header "User-Agent: Replayed by Gor"
    
  13. 重写请求 URL

    gor --input-raw :8080 --output-http staging.com --http-rewrite-url /v1/user/([^\\/]+)/ping:/v2/user/$1/ping
    
  14. 设置 URL 参数

    gor --input-raw :8080 --output-http staging.com --http-set-param api_key=1
    
  15. 设置请求头

    gor --input-raw :80 --output-http "http://staging.server" --http-header "User-Agent: Replayed by Gor" --http-header "Enable-Feature-X: true"
    
  16. 导出到 Elasticsearch(ES)

    ./gor --input-raw :8000 --output-http http://staging.com  --output-http-elasticsearch localhost:9200/gor
    
  17. 基于 Header 或 URL 参数值的一致限制

    如果您在 Header 或 URL 中存储了唯一的用户 ID(例如 API 密钥),则可以仅针对该用户的一部分持续转发指定的流量百分比。基本公式如下:\(FNV32 - 1A\_hashing(value) \% 100 >= chance\)。示例如下:

    # 基于 Header 值进行限制
    sudo ./gor --input-raw :80 --output-tcp "replay.local:28020|10%" --http-header-limiter "X-API-KEY: 10%"
    # 基于 URL 参数值进行限制
    sudo ./gor --input-raw :80 --output-tcp "replay.local:28020|10%" --http-param-limiter "api_key: 10%"
    

需要注意的是,当基于 Header 或参数进行限制时,仅支持基于百分比的限制。

限速

Goreplay 具备强大的功能,支持对捕获到的生产实际请求流量进行调整,既可以减少流量,也能够放大流量后重放至测试环境,以此开展压力测试。压力测试通常围绕对输入流量的减少或放大来进行。示例如下:

# 以 2 倍速度从文件中重放请求流量
gor --input-file "requests.gor|200%" --output-http "staging.com"

当然,它也支持将请求流量缩小至 10%、20% 等比例。

若受限于测试环境服务器的资源压力,你仅希望将部分流量重放至测试环境,而非全部生产实际流量,那么就可以借助 Goreplay 的限速功能。该功能提供了两种策略来实现限流:

  1. 随机丢弃请求流量
  2. 基于 Header 或者 URL 按一定百分比丢弃流量

随机丢弃请求流量

在输入(input)和输出(output)两端,Goreplay 均支持限速操作,并且提供了两种限速算法,分别是百分比和绝对值:

  • 百分比:在输入端,此算法支持缩小或者放大请求流量,会依据指定的策略随机丢弃部分请求流量。
  • 绝对值:若在单位时间(秒)内请求数量达到临界值,后续的请求流量将被丢弃,待下一秒来临,临界值会恢复初始状态。

使用方法

在输出端,可以使用 “|” 运算符来指定限速阈值。示例如下:

  • 使用绝对值限速(当在输入端使用绝对值时,对应的是每秒请求数,即 QPS)
# staging 服务器每秒接收的请求不超过 10 个
gor --input-tcp :28020 --output-http "http://staging.com|10"
  • 使用百分比限速
# 重放服务器接收的请求不超过总请求量的 10%,适用于高负载环境
gor --input-raw :80 --output-tcp "replay.local:28020|10%"

基于 Header 或者 URL 参数限速

若请求的 Header 或者 URL 参数中包含唯一值,例如 API 密钥,那么就可以将指定百分比的流量转发至后端服务器。示例如下:

# 基于 Header 值进行流量限制
gor --input-raw :80 --output-tcp "replay.local:28020|10%" --http-header-limiter "X-API-KEY: 10%"
# 基于 URL 参数值进行流量限制
gor --input-raw :80 --output-tcp "replay.local:28020|10%" --http-param-limiter "api_key: 10%"

注意事项

  1. 若 HTTP 请求不符合规范,可能会导致无法捕获到数据包。例如,曾出现过 HTTP 请求头中的 Content-Length 与实际的 Body 大小不相等的情况,此时 Goreplay 会认为请求尚未结束。
  2. input-file 是由单 goroutine 运行的,这可能会造成性能瓶颈。在测试过程中,读取的每秒请求数(RPS)大约在 1.7 万 - 1.8 万左右。如果压测需求超过这个数值,就需要开启多个进程同时运行。需要注意的是,多个输入之间是并行处理的,但单个输入对应多个输出时,是串行处理的。所有输入都实现了 io.Reader 接口,输出都实现了 io.Writer 接口。因此,在阅读代码时,输入的入口是 Read() 方法,输出的入口是 Write() 方法。

六、总结

今天我们简要介绍了 Go replay 的常见使用方式,实际上,Goreplay 的功能远不止上述所提及的这些。后续有时间,我们将继续探讨更多的使用技巧。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件举报,一经查实,本站将立刻删除。

文章由技术书栈整理,本文链接:https://study.disign.me/article/202511/1.goreplay-install.md

发布时间: 2025-03-10