231 lines
9.4 KiB
Markdown
231 lines
9.4 KiB
Markdown
### 技术选型
|
||
- **语言**:Go。
|
||
- **HTTP客户端**:
|
||
- 抽象接口:`HTTPClient`。
|
||
- 实现:
|
||
- `fasthttp`(`github.com/valyala/fasthttp`):高性能,优先选择。
|
||
- `net/http`(标准库):兼容性强,备选。
|
||
- **Tokenizer 库**:`github.com/tiktoken-go/tokenizer`。
|
||
- **并发管理**:goroutine池、`sync.Pool`、`context.Context`。
|
||
- **数据处理**:`sync.Map`存储响应数据,`gonum/stat`计算百分位数。
|
||
- **图表生成**:`go-echarts`。
|
||
|
||
### 产品需求设计
|
||
|
||
#### 功能需求
|
||
- **API兼容性**:
|
||
- 支持OpenAI Compatible风格的LLM API(包括`stream=true`的SSE流式响应)。
|
||
- 兼容推理模型(如DeepSeek-R1)的响应结构,包含`reasoning_content`字段(与`content`同级)。
|
||
- **HTTP客户端抽象层**:
|
||
- 定义`HTTPClient`接口,包含以下方法:
|
||
- `Do(req *Request) (*Response, error)`:发送非流式请求。
|
||
- `Stream(req *Request, callback func(chunk SSEChunk) error) error`:处理流式响应,逐块回调SSE数据。
|
||
- 实现:
|
||
- `fasthttp`:基于`fasthttp.RequestCtx`和`Response.BodyStream`。
|
||
- `net/http`:基于`http.Client`和`Response.Body`。
|
||
- 配置项`client: "fasthttp"`或`client: "net-http"`切换客户端。
|
||
- **提示词生成**:
|
||
- 使用`tiktoken-go/tokenizer`动态生成短咨询提示词(50 tokens ±5%)和长文档提示词(1000 tokens ±5%)。
|
||
- **并发请求**:
|
||
- 支持最大500并发,持续5分钟(300秒)或更长时间。
|
||
- 使用goroutine池(最大600 goroutine),`sync.Pool`重用请求对象。
|
||
- 通过`context.Context`支持任务终止。
|
||
- **场景建模**:
|
||
- 混合负载:短咨询(50 tokens,70%比例),长文档生成(1000 tokens,30%比例)。
|
||
- **用户行为模拟**:请求间隔遵循泊松分布(`math/rand`实现)。
|
||
- **梯度增压**:支持阶梯式加压(50→200→500并发),每阶段持续300秒。
|
||
- **性能指标收集**:
|
||
- **请求统计**:总请求数、成功请求数、失败请求数、超时比率(目标:<1%,默认超时30-60秒)。
|
||
- **响应时间**:平均、最小/最大、P90/P95/P99(目标:<2秒至<10秒,<3秒至<15秒,<5秒至<20秒)。
|
||
- **TTFT**:
|
||
- 最小/最大TTFT,P90/P95/P99 TTFT。
|
||
- 通过SSE流式响应测量,精度达毫秒,兼容`content`和`reasoning_content`。
|
||
- **QPS**:平均QPS(目标:50-200+),最大QPS。
|
||
- **Token生成速率**:平均速率(目标:>100 tokens/秒),最大速率,兼容`content`和`reasoning_content`。
|
||
- **最大有效并发用户数**:记录仍有请求成功的最大并发数(目标:50-500+)。
|
||
- **报告生成**:
|
||
- 生成质量报告,包含所有指标统计。
|
||
- 提供图表(如响应时间分布、TTFT分布、QPS曲线)。
|
||
|
||
#### 非功能需求
|
||
- **高性能**:
|
||
- 支持500并发,持续5分钟以上,`fasthttp`优化吞吐量,goroutine池降低GC压力。
|
||
- **统计准确性**:
|
||
- TTFT和响应时间测量精度达毫秒,使用`time.Now().UnixNano()`。
|
||
- 使用`sync.Mutex`或`sync/atomic`确保并发统计线程安全。
|
||
- **可扩展性**:支持新服务提供商、提示词模板、响应字段。
|
||
- **易用性**:配置文件指定API、提示词、超时、客户端类型(`fasthttp`或`net-http`)。
|
||
- **稳定性**:长时间运行无崩溃、死锁或内存泄漏。
|
||
- **可观测性**:
|
||
- 使用`log`包记录关键事件和错误,通过`--debug`参数,开启调试日志,记录详细信息。
|
||
- 使用`runtime`包监控CPU、内存、goroutine数。
|
||
- 使用`sync.Map`存储响应数据,`gonum/stat`计算百分位数。
|
||
|
||
#### 技术实现建议
|
||
- **HTTP客户端抽象层**:
|
||
- 定义`HTTPClient`接口:
|
||
```go
|
||
type HTTPClient interface {
|
||
Do(req *Request) (*Response, error)
|
||
Stream(req *Request, callback func(chunk SSEChunk) error) error
|
||
}
|
||
type Request struct {
|
||
Method string
|
||
URL string
|
||
Body []byte
|
||
Headers map[string]string
|
||
}
|
||
type Response struct {
|
||
StatusCode int
|
||
Body []byte
|
||
Headers map[string]string
|
||
}
|
||
type SSEChunk struct {
|
||
Data []byte // JSON数据
|
||
Timestamp int64 // 接收时间戳(UnixNano)
|
||
IsDone bool // 是否为[data: [DONE]]
|
||
}
|
||
```
|
||
- **fasthttp实现**:
|
||
- `Do`:使用`fasthttp.Do`,构造OpenAI Compatible JSON请求,解析响应。
|
||
- `Stream`:使用`fasthttp.Response.BodyStream`,逐行解析SSE(`data: {...}`),调用回调函数传递`SSEChunk`。
|
||
- 使用`sync.Pool`重用`fasthttp.Request`和`fasthttp.Response`。
|
||
- **net/http实现**:
|
||
- `Do`:使用`http.Client.Do`,构造请求,解析响应。
|
||
- `Stream`:使用`bufio.Scanner`读取`http.Response.Body`,解析SSE,调用回调函数。
|
||
- **切换逻辑**:
|
||
- 解析配置项`client`,实例化对应客户端:
|
||
```go
|
||
func NewHTTPClient(clientType string) (HTTPClient, error) {
|
||
switch clientType {
|
||
case "fasthttp":
|
||
return &FastHTTPClient{}, nil
|
||
case "net-http":
|
||
return &NetHTTPClient{}, nil
|
||
default:
|
||
return nil, fmt.Errorf("unsupported client: %s", clientType)
|
||
}
|
||
}
|
||
```
|
||
- **SSE与TTFT处理**:
|
||
- `Stream`方法逐块解析SSE,提取`choices[0].delta.content`或`choices[0].delta.reasoning_content`。
|
||
- 记录请求发送时间(`t0 = time.Now().UnixNano()`)。
|
||
- 检测首个非空Token块,记录时间(`t1`),TTFT = (`t1 - t0`) / 1e6(毫秒)。
|
||
- 兼容推理模型:优先检查`reasoning_content`,若为空则使用`content`。
|
||
- **Go性能优化**:
|
||
- **Goroutine池**:使用`ants`库或自定义池,限制最大600 goroutine。
|
||
- **对象池**:`sync.Pool`重用请求/响应对象。
|
||
- **上下文控制**:`context.WithTimeout`设置压测时长(默认300秒)。
|
||
- **资源监控**:使用`runtime`包记录CPU、内存、goroutine数。
|
||
- **统计准确性**:
|
||
- 使用`sync/atomic`累加请求计数。
|
||
- 使用`sync.Mutex`保护响应时间和TTFT切片。
|
||
- 使用`gonum/stat`计算P90/P95/P99。
|
||
- **提示词生成**:`tiktoken-go/tokenizer`动态调整模板至50或1000 tokens。
|
||
- **报告生成**:`go-echarts`生成图表,模板引擎生成HTML/PDF。
|
||
|
||
#### 压测流程
|
||
1. **配置加载**:读取API端点、客户端类型(`fasthttp`或`net-http`)、并发数、提示词模板等。
|
||
2. **初始化HTTP客户端**:根据配置项`client`实例化`fasthttp`或`net/http`客户端。
|
||
3. **提示词生成**:使用`tiktoken-go/tokenizer`生成短咨询(50 tokens)和长文档(1000 tokens)提示词。
|
||
4. **并发执行**:
|
||
- 初始化goroutine池(最大600)。
|
||
- 使用`HTTPClient.Stream`发送请求(`stream=true`),间隔符合泊松分布。
|
||
- 处理SSE流,记录TTFT。
|
||
5. **响应处理**:
|
||
- 解析`content`和`reasoning_content`,计算Token数。
|
||
- 记录响应时间、TTFT、成功/失败状态。
|
||
6. **指标计算**:
|
||
- 计算请求统计、响应时间、TTFT、QPS、Token生成速率、最大并发用户数。
|
||
7. **报告生成**:输出统计数据和图表。
|
||
|
||
#### 配置示例与报告内容
|
||
以下是更新后的配置和报告示例,包含HTTP客户端抽象层和切换逻辑:
|
||
|
||
```yaml
|
||
# 配置示例
|
||
api:
|
||
endpoint: "https://api.example.com/v1/completions"
|
||
api_key: "your_api_key"
|
||
model: "deepseek-r1" # 支持推理模型
|
||
streaming: true # 启用流式响应
|
||
client: "fasthttp" # HTTP客户端:fasthttp 或 net-http
|
||
|
||
prompt_templates:
|
||
short:
|
||
target_tokens: 50
|
||
templates:
|
||
- "What is the capital of {country}?"
|
||
- "Please briefly explain the concept of {concept}."
|
||
- "Summarize the main idea of {topic} in one sentence."
|
||
long:
|
||
target_tokens: 1000
|
||
templates:
|
||
- "Write a detailed history of {country} covering major events."
|
||
- "Compose an in-depth analysis of {topic} with examples."
|
||
- "Generate a comprehensive report on the impact of {event}."
|
||
|
||
requests:
|
||
- type: "short"
|
||
weight: 0.7
|
||
- type: "long"
|
||
weight: 0.3
|
||
|
||
concurrency:
|
||
steps: [50, 200, 500]
|
||
duration_per_step: 300 # 秒,5分钟
|
||
max_goroutines: 600 # 最大goroutine数
|
||
|
||
timeout: 60 # 秒
|
||
poisson_lambda: 1.0 # 请求间隔泊松分布参数
|
||
|
||
tokenizer:
|
||
model: "gpt-3.5-turbo" # 用于tiktoken-go的分词器模型
|
||
|
||
# 报告内容示例
|
||
report:
|
||
overview:
|
||
api_endpoint: "https://api.example.com/v1/completions"
|
||
model: "deepseek-r1"
|
||
concurrency_steps: [50, 200, 500]
|
||
duration_per_step: "300s"
|
||
request_mix: "70% short (50 tokens), 30% long (1000 tokens)"
|
||
tokenizer: "tiktoken-go (gpt-3.5-turbo)"
|
||
streaming_enabled: true
|
||
http_client: "fasthttp"
|
||
metrics:
|
||
total_requests: 15000
|
||
successful_requests: 14850
|
||
failed_requests: 150
|
||
timeout_ratio: "1.0%"
|
||
response_time:
|
||
avg: "1.8s"
|
||
min: "0.5s"
|
||
max: "10.2s"
|
||
p90: "2.5s"
|
||
p95: "3.0s"
|
||
p99: "4.5s"
|
||
ttft:
|
||
min: "0.1s"
|
||
max: "2.0s"
|
||
p90: "0.3s"
|
||
p95: "0.4s"
|
||
p99: "0.6s"
|
||
qps:
|
||
avg: 180
|
||
max: 220
|
||
token_rate:
|
||
avg: "120 tokens/s"
|
||
max: "150 tokens/s"
|
||
max_concurrent_users: 500
|
||
resource_usage:
|
||
avg_cpu: "60%"
|
||
max_memory: "1.1GB"
|
||
max_goroutines: 600
|
||
charts:
|
||
- "response_time_distribution.png"
|
||
- "ttft_distribution.png"
|
||
- "qps_over_time.png"
|
||
- "token_rate_over_time.png"
|
||
- "concurrency_vs_response_time.png"
|
||
``` |