1. 性能基准与目标:定义评分接口的延迟指标

性能优化不能靠感觉,必须从可量化的指标和可复现的压测开始。本章将定义评分接口的核心延迟指标(TP50、TP99、TP999),设定清晰的优化目标,并搭建标准化的压测环境。这是整个优化旅程的基准线,后续所有优化手段的效果都将以此对比验证。

1.1 核心延迟指标定义

在低延迟场景中,平均延迟(Avg)容易被极端值掩盖,无法真实反映用户体验。我们采用百分位延迟(Percentile)作为核心度量标准,重点关注以下三个分位值:

指标 含义 业务解读 典型优化目标
TP50 50% 的请求延迟 ≤ 该值 半数用户的体验基线,反映系统正常负载下的响应速度 ≤ 50ms
TP99 99% 的请求延迟 ≤ 该值 绝大多数用户的体验上限,反映系统在典型压力下的稳定性 ≤ 150ms
TP999 99.9% 的请求延迟 ≤ 该值 极端尾部延迟,反映系统抗抖动能力和慢请求治理效果 ≤ 500ms
实践要点: 优先优化 TP99 和 TP999。TP50 容易通过缓存等手段快速达标,但尾部延迟往往隐藏着数据库连接池耗尽、GC 停顿、网络重传等深层问题,是性能优化的主战场。

1.2 设定量化优化目标

基于当前线上监控数据(假设基线:TP50=120ms, TP99=450ms, TP999=1200ms),我们设定分阶段优化目标:

  • 第一阶段(快速见效): TP50 ≤ 50ms,TP99 ≤ 200ms。通过本地缓存、连接池调优、SQL 索引优化实现。
  • 第二阶段(深度优化): TP99 ≤ 150ms,TP999 ≤ 500ms。通过异步化、预计算、热点分离、GC 调优实现。
  • 第三阶段(极致稳定): TP999 ≤ 200ms,零抖动窗口 ≥ 99.99%。通过多级缓存、熔断降级、零拷贝、线程模型重构实现。
目标设定原则: 每个阶段的目标必须可测量、可验证,且与业务 SLA 对齐。避免设定“提升性能”这类模糊目标,应精确到“TP99 从 450ms 降至 150ms”。

1.3 搭建压测环境

我们选择 wrk 作为主要压测工具(轻量、高性能、适合 HTTP 接口),并辅以 Locust 进行复杂场景模拟。压测环境需与生产环境隔离,但硬件配置、网络拓扑应尽量接近。

1.3.1 压测环境拓扑

评分接口压测环境拓扑 评分接口压测环境拓扑 压测客户端 wrk / Locust 并发连接数: 200~1000 HTTP 请求 Nginx 网关 负载均衡 / 限流 应用服务器 评分服务实例 x 2 ~ 4 节点 JVM / Go Runtime Redis 缓存 MySQL 主库 JDBC 连接 Lettuce / Jedis

1.3.2 wrk 压测脚本

wrk 适合快速压测,使用 Lua 脚本可定制请求参数。以下是一个针对评分接口的压测脚本示例:

-- wrk_benchmark.lua
-- 使用方法: wrk -t8 -c200 -d60s -s wrk_benchmark.lua http://target-host/score

-- 请求参数模板
local request_body = '{"user_id": 12345, "item_id": 67890, "score_type": "comprehensive"}'

-- 初始化随机种子
math.randomseed(os.time())

-- 每个请求前调用
function request()
    -- 动态替换 user_id 模拟真实用户
    local uid = math.random(1, 100000)
    local body = string.gsub(request_body, "12345", tostring(uid))
    return wrk.format("POST", "/v1/score/evaluate", 
        {["Content-Type"] = "application/json"}, body)
end

-- 响应处理(可选,用于统计)
function response(status, headers, body)
    if status ~= 200 then
        -- 记录非 200 响应,用于错误率统计
        -- 实际生产可写入日志
    end
end

执行压测命令:

# 8 线程,200 并发连接,持续 60 秒
wrk -t8 -c200 -d60s -s wrk_benchmark.lua http://localhost:8080/v1/score/evaluate

1.3.3 Locust 压测脚本

Locust 适合模拟更复杂的用户行为,如登录、评分、查询混合场景:

# locustfile.py
from locust import HttpUser, task, between
import random

class ScoreUser(HttpUser):
    wait_time = between(0.5, 2.0)  # 模拟用户思考时间
    
    def on_start(self):
        """每个用户启动时执行一次"""
        self.user_id = random.randint(1, 100000)
        self.token = self._login()
    
    def _login(self):
        """模拟登录获取 token"""
        resp = self.client.post("/auth/login", 
            json={"username": f"user_{self.user_id}", "password": "test123"})
        return resp.json().get("token", "")
    
    @task(3)  # 权重 3:评分接口
    def evaluate_score(self):
        """评分接口压测"""
        headers = {"Authorization": f"Bearer {self.token}"}
        payload = {
            "user_id": self.user_id,
            "item_id": random.randint(1, 50000),
            "score_type": random.choice(["comprehensive", "quality", "service"])
        }
        with self.client.post("/v1/score/evaluate", 
            json=payload, headers=headers, catch_response=True) as resp:
            if resp.elapsed.total_seconds() > 0.5:
                resp.failure(f"TP99 超标: {resp.elapsed.total_seconds():.3f}s")
    
    @task(1)  # 权重 1:查询历史评分
    def query_history(self):
        """查询历史评分"""
        headers = {"Authorization": f"Bearer {self.token}"}
        self.client.get(f"/v1/score/history?user_id={self.user_id}", 
            headers=headers)

启动 Locust Web 界面:

# 启动 Web 界面,通过浏览器 http://localhost:8089 控制
locust -f locustfile.py --host=http://localhost:8080 --web-host=0.0.0.0 --web-port=8089

1.4 压测执行与数据采集

压测执行需遵循以下步骤,确保数据可复现:

  1. 预热阶段: 以 50% 目标并发运行 30 秒,让 JIT、连接池、缓存达到稳定状态。
  2. 正式压测: 以目标并发(如 200、500、1000)运行 60~120 秒,记录完整延迟分布。
  3. 数据采集: 收集 wrk/Locust 输出的延迟分布、错误率、吞吐量(RPS)。
  4. 资源监控: 同步采集 CPU、内存、GC、网络 IO、DB 连接池等指标,用于后续瓶颈分析。
常见陷阱:
  • 压测客户端自身成为瓶颈(CPU 打满导致请求发不出去)。建议使用多台压测机,或 wrk 的线程数不超过 CPU 核心数。
  • 忽略网络延迟差异。压测机应与目标服务器部署在同一机房或同一 VPC 内,避免公网抖动干扰结果。
  • 只压一次就下结论。至少压测 3 次取中位数,排除偶然抖动。

1.5 基准数据记录模板

每次压测后,将结果记录到统一的基准表中,便于后续对比:

压测日期 并发数 TP50 (ms) TP99 (ms) TP999 (ms) Avg (ms) RPS 错误率 备注
2025-03-01 200 118 445 1180 95 2100 0.02% 基线版本 v1.0
2025-03-01 500 245 890 2100 210 3800 0.15% 基线版本 v1.0
— — — — — — — — 优化后填入

1.6 延迟分布可视化

为了直观理解延迟分布,我们绘制一个典型的评分接口延迟分布图(基于基线数据):

评分接口延迟分布(基线) 评分接口延迟分布(基线,并发 200) 请求占比 (%) 0% 20% 40% 60% 80% 35% 0~50ms 30% 50~100ms 20% 100~200ms 10% 200~500ms 4% 500~1000ms 1% >1000ms 说明 TP50: 118ms TP99: 445ms TP999: 1180ms Avg: 95ms