golang自动化测试网络环境模拟代理插件toxiproxy的使用
Golang自动化测试网络环境模拟代理插件Toxiproxy的使用
Toxiproxy是一个用于模拟网络条件的框架,专门用于测试、CI和开发环境,支持对连接进行可预测的干扰,同时支持随机化的混乱和自定义配置。
安装Toxiproxy
Linux
可以从Toxiproxy的GitHub Releases页面下载最新二进制文件或系统包。
Ubuntu
wget -O toxiproxy-2.1.4.deb https://github.com/Shopify/toxiproxy/releases/download/v2.1.4/toxiproxy_2.1.4_amd64.deb
sudo dpkg -i toxiproxy-2.1.4.deb
sudo service toxiproxy start
OS X (使用Homebrew)
brew tap shopify/shopify
brew install toxiproxy
Docker
docker pull ghcr.io/shopify/toxiproxy
docker run --rm -it ghcr.io/shopify/toxiproxy
从源码构建
make build
./toxiproxy-server
Golang客户端示例
下面是一个完整的Golang示例,展示如何使用Toxiproxy模拟网络延迟:
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/Shopify/toxiproxy/v2/client"
)
func main() {
// 创建Toxiproxy客户端
toxiproxyClient := client.NewClient("http://localhost:8474")
// 创建代理
proxy, err := toxiproxyClient.CreateProxy("test_redis", "localhost:26379", "localhost:6379")
if err != nil {
log.Fatal(err)
}
// 添加延迟toxic
_, err = proxy.AddToxic("latency_downstream", "latency", "downstream", 1.0, client.Attributes{
"latency": 1000, // 1000毫秒延迟
"jitter": 100, // 100毫秒抖动
})
if err != nil {
log.Fatal(err)
}
// 测试Redis连接
start := time.Now()
resp, err := http.Get("http://localhost:26379")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("请求耗时: %v\n", time.Since(start))
// 清理
err = proxy.Delete()
if err != nil {
log.Fatal(err)
}
}
核心概念
-
代理(Proxy):Toxiproxy在客户端和上游服务之间创建的TCP代理
name
: 代理名称listen
: 监听地址upstream
: 上游服务地址enabled
: 是否启用
-
Toxics(干扰器):应用于代理连接的干扰规则
- 常见类型:latency(延迟)、bandwidth(带宽限制)、timeout(超时)等
- 可以设置方向(upstream/downstream)和应用概率(toxicity)
常用Toxics
延迟(latency)
// 添加1000ms延迟 ±100ms抖动
_, err = proxy.AddToxic("latency", "latency", "downstream", 1.0, client.Attributes{
"latency": 1000,
"jitter": 100,
})
带宽限制(bandwidth)
// 限制带宽为100KB/s
_, err = proxy.AddToxic("slow_bandwidth", "bandwidth", "downstream", 1.0, client.Attributes{
"rate": 100, // KB/s
})
超时(timeout)
// 设置1000ms后超时
_, err = proxy.AddToxic("timeout", "timeout", "downstream", 1.0, client.Attributes{
"timeout": 1000,
})
HTTP API
Toxiproxy提供RESTful API管理代理和Toxics,主要端点包括:
GET /proxies
- 列出所有代理POST /proxies
- 创建新代理POST /proxies/{proxy}/toxics
- 为代理添加ToxicDELETE /proxies/{proxy}
- 删除代理
测试示例
func TestRedisWithLatency(t *testing.T) {
// 创建Toxiproxy客户端
toxiproxyClient := client.NewClient("http://localhost:8474")
// 创建Redis代理
proxy, err := toxiproxyClient.CreateProxy("test_redis", "localhost:26379", "localhost:6379")
if err != nil {
t.Fatal(err)
}
defer proxy.Delete()
// 添加延迟
_, err = proxy.AddToxic("latency", "latency", "downstream", 1.0, client.Attributes{
"latency": 1000,
})
if err != nil {
t.Fatal(err)
}
// 测试Redis连接
start := time.Now()
conn, err := redis.Dial("tcp", "localhost:26379")
if err != nil {
t.Fatal(err)
}
defer conn.Close()
if time.Since(start) < time.Second {
t.Error("延迟未生效")
}
}
Toxiproxy是验证应用程序弹性和容错能力的强大工具,特别适合在自动化测试中模拟各种网络异常情况。
更多关于golang自动化测试网络环境模拟代理插件toxiproxy的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang自动化测试网络环境模拟代理插件toxiproxy的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用Toxiproxy进行Golang网络环境模拟测试
Toxiproxy是一个强大的网络代理工具,专门用于模拟各种网络环境条件,非常适合在自动化测试中模拟网络故障、延迟、限制等情况。下面我将介绍如何在Golang项目中使用Toxiproxy进行网络环境模拟测试。
1. Toxiproxy简介
Toxiproxy提供以下主要功能:
- 延迟注入
- 带宽限制
- 连接断开
- 数据包丢弃
- 超时模拟
2. 安装Toxiproxy
首先需要安装Toxiproxy服务:
# 使用Docker安装
docker pull ghcr.io/shopify/toxiproxy:latest
docker run -d -p 8474:8474 -p 9000-9100:9000-9100 ghcr.io/shopify/toxiproxy
3. Golang客户端库
安装Golang的Toxiproxy客户端库:
go get github.com/Shopify/toxiproxy/v2/client
4. 基本使用示例
4.1 创建代理
package main
import (
"fmt"
"log"
"time"
"github.com/Shopify/toxiproxy/v2/client"
)
func main() {
// 创建Toxiproxy客户端
toxiproxyClient := client.NewClient("localhost:8474")
// 创建一个代理,将本地9000端口转发到example.com的80端口
proxy, err := toxiproxyClient.CreateProxy("web", "localhost:9000", "example.com:80")
if err != nil {
log.Fatal(err)
}
// 添加1000ms的延迟和10%的数据包丢失
_, err = proxy.AddToxic("latency_down", "latency", "downstream", 1.0, map[string]interface{}{
"latency": 1000,
"jitter": 100,
})
if err != nil {
log.Fatal(err)
}
_, err = proxy.AddToxic("loss_down", "limit_data", "downstream", 1.0, map[string]interface{}{
"bytes": 1000, // 每1000字节后断开连接
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Proxy created with latency and packet loss")
time.Sleep(10 * time.Minute) // 保持代理运行
}
4.2 测试HTTP请求
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
"github.com/Shopify/toxiproxy/v2/client"
)
func testHTTPRequest() {
// 注意我们使用的是Toxiproxy创建的代理端口(9000)而不是直接连接目标
resp, err := http.Get("http://localhost:9000")
if err != nil {
log.Printf("HTTP request failed: %v", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Failed to read response: %v", err)
return
}
fmt.Printf("Response status: %s, Body length: %d\n", resp.Status, len(body))
}
func main() {
toxiproxyClient := client.NewClient("localhost:8474")
// 创建代理
proxy, err := toxiproxyClient.CreateProxy("http_test", "localhost:9000", "httpbin.org:80")
if err != nil {
log.Fatal(err)
}
// 测试正常请求
fmt.Println("Testing normal request...")
testHTTPRequest()
// 添加延迟
fmt.Println("\nAdding 2s latency...")
_, err = proxy.AddToxic("latency", "latency", "downstream", 1.0, map[string]interface{}{
"latency": 2000,
})
if err != nil {
log.Fatal(err)
}
testHTTPRequest()
// 添加超时
fmt.Println("\nAdding timeout...")
_, err = proxy.AddToxic("timeout", "timeout", "downstream", 1.0, map[string]interface{}{
"timeout": 1000,
})
if err != nil {
log.Fatal(err)
}
testHTTPRequest()
// 重置所有toxics
fmt.Println("\nResetting all toxics...")
err = proxy.ResetState()
if err != nil {
log.Fatal(err)
}
testHTTPRequest()
}
5. 在单元测试中使用Toxiproxy
下面是一个完整的单元测试示例,展示如何在测试中模拟网络问题:
package main
import (
"net/http"
"testing"
"time"
"github.com/Shopify/toxiproxy/v2/client"
"github.com/stretchr/testify/assert"
)
func TestHttpClientWithToxiproxy(t *testing.T) {
// 初始化Toxiproxy客户端
toxiproxyClient := client.NewClient("localhost:8474")
// 创建代理
proxy, err := toxiproxyClient.CreateProxy("test_proxy", "localhost:9001", "httpbin.org:80")
assert.NoError(t, err)
defer proxy.Delete() // 测试结束后删除代理
// 测试用例1: 正常请求
t.Run("normal request", func(t *testing.T) {
client := http.Client{Timeout: 5 * time.Second}
resp, err := client.Get("http://localhost:9001/get")
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, resp.StatusCode)
})
// 测试用例2: 带延迟的请求
t.Run("request with latency", func(t *testing.T) {
// 添加200ms延迟
_, err := proxy.AddToxic("latency", "latency", "downstream", 1.0, map[string]interface{}{
"latency": 200,
})
assert.NoError(t, err)
start := time.Now()
client := http.Client{Timeout: 1 * time.Second}
_, err = client.Get("http://localhost:9001/get")
assert.NoError(t, err)
assert.GreaterOrEqual(t, time.Since(start).Milliseconds(), int64(200))
})
// 测试用例3: 模拟超时
t.Run("request timeout", func(t *testing.T) {
// 添加3秒延迟,但客户端只等待1秒
_, err := proxy.AddToxic("high_latency", "latency", "downstream", 1.0, map[string]interface{}{
"latency": 3000,
})
assert.NoError(t, err)
client := http.Client{Timeout: 1 * time.Second}
_, err = client.Get("http://localhost:9001/get")
assert.Error(t, err)
assert.Contains(t, err.Error(), "context deadline exceeded")
})
// 重置代理状态
err = proxy.ResetState()
assert.NoError(t, err)
}
6. 高级用法
6.1 模拟带宽限制
// 限制下游带宽为100KB/s
_, err = proxy.AddToxic("bandwidth", "bandwidth", "downstream", 1.0, map[string]interface{}{
"rate": 100 * 1024, // 字节/秒
})
6.2 模拟连接断开
// 模拟50%的连接断开
_, err = proxy.AddToxic("slicer", "slicer", "downstream", 1.0, map[string]interface{}{
"average_size": 1024,
"size_variation": 512,
"delay": 100,
})
7. 最佳实践
- 测试隔离:每个测试用例应该创建自己的代理,避免相互干扰
- 资源清理:测试完成后删除代理和toxics
- 组合使用:可以组合多种toxics模拟复杂网络环境
- 真实场景:根据实际用户网络条件设计测试场景
Toxiproxy为Golang应用的网络可靠性测试提供了强大工具,能够帮助开发者发现和修复各种网络边界条件下的问题。