OpenClaw 性能调优实战:让你的服务吞吐提升 5 倍
引言
最近我们把一个基于 OpenClaw 的推荐服务从 800 QPS 优化到了 4200 QPS(相同 4 核 8G 机器)。本文复盘整个调优过程,把可复用的方法论沉淀下来。
一、调优前的性能基线
压测脚本(k6):
import http from 'k6/http';
import { check } from 'k6';
export const options = {
vus: 100,
duration: '60s',
};
export default function () {
const res = http.get('http://localhost:8080/api/recommend?user_id=12345');
check(res, {
'status is 200': (r) => r.status === 200,
'latency < 200ms': (r) => r.timings.duration < 200,
});
}
基线结果:
| 指标 | 数值 |
|---|---|
| QPS | 812 |
| P50 延迟 | 95ms |
| P95 延迟 | 380ms |
| P99 延迟 | 920ms |
| CPU 使用率 | 95% |
| 内存占用 | 4.2G |
二、第一步:开启 pprof
import _ "net/http/pprof"
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
启动后访问 http://localhost:6060/debug/pprof/,可以看到 30s CPU profile:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
在 pprof 交互界面输入 top,看到 CPU 热点:
Showing nodes accounting for 21.4s, 78.2% of 27.4s total
flat flat% sum% cum cum%
5.6s 20.4% 20.4% 7.2s 26.3% runtime.mallocgc
3.8s 13.9% 34.3% 3.8s 13.9% encoding/json.Unmarshal
2.9s 10.6% 44.9% 4.1s 15.0% github.com/opencLaw/.../FeatureEngine.Score
2.4s 8.8% 53.7% 2.4s 8.8% github.com/opencLaw/.../UserProfile.Load
Top 3 热点:
runtime.mallocgc—— 频繁分配内存encoding/json.Unmarshal—— JSON 反序列化FeatureEngine.Score—— 推荐打分逻辑
三、调优动作 1:sync.Pool 减少内存分配
JSON 反序列化会大量分配临时对象,用 sync.Pool 复用:
var decoderPool = sync.Pool{
New: func() interface{} {
return json.NewDecoder(nil)
},
}
func parseRequest(r io.Reader, v interface{}) error {
dec := decoderPool.Get().(*json.Decoder)
defer decoderPool.Put(dec)
dec.Reset(r)
return dec.Decode(v)
}
效果:CPU 占用下降 18%。
四、调优动作 2:缓存热点用户画像
UserProfile.Load 是远程调用,每次都查 DB 会很慢。增加本地缓存:
type profileCache struct {
mu sync.RWMutex
data map[int64]*UserProfile
ttl time.Duration
}
func (c *profileCache) Get(userID int64) (*UserProfile, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
p, ok := c.data[userID]
return p, ok
}
配合 LRU 淘汰策略,命中率 93%。
五、调优动作 3:算法向量化
FeatureEngine.Score 原本是循环逐个特征计算,改用矩阵运算(gonum 库):
// 优化前:循环
for i := 0; i < len(features); i++ {
score += weights[i] * features[i]
}
// 优化后:向量化
scoreVec := mat.NewVecDense(len(features), features)
weightVec := mat.NewVecDense(len(weights), weights)
scoreVec.MulElem(weightVec, scoreVec)
score := mat.Sum(scoreVec)
P95 延迟下降 62%。
六、调优动作 4:GOGC 与 GOMEMLIMIT
GOGC=200 GOMEMLIMIT=6GiB ./opencLaw
GOGC=200:GC 触发阈值翻倍,减少 GC 频率GOMEMLIMIT:软限制内存,避免 OOM
实测 GC 时间从平均 18ms 降到 4ms。
七、调优后效果
| 指标 | 调优前 | 调优后 | 提升 |
|---|---|---|---|
| QPS | 812 | 4218 | 5.2x |
| P50 延迟 | 95ms | 22ms | 4.3x |
| P95 延迟 | 380ms | 78ms | 4.9x |
| P99 延迟 | 920ms | 195ms | 4.7x |
| CPU 使用率 | 95% | 78% | ↓ 17% |
| 内存占用 | 4.2G | 5.1G | ↑ 21% |
⚠️ 内存略升,是因为缓存了用户画像数据。属合理 trade-off。
八、调优方法论总结
- 先量后优:始终从 pprof / trace 出发,找到真正的瓶颈
- 由易到难:先参数调优(GOGC、连接池),后代码重构
- 分步验证:每次只改一处,验证后再继续
- 关注 trade-off:吞吐 vs 延迟 vs 内存,没有银弹