压力测试实战:基于Locust的高并发场景稳定性验证

所有的架构设计和代码优化,最终都要在压力测试的烈火中接受检验。对于DeepSeek推理服务,我们不能简单地用 abwrk 这种针对静态网页的工具来测,因为大模型的请求是长连接,且计算负载与Prompt长度高度相关。

Locust 是一个基于Python的开源压测工具,它允许我们编写Python代码来模拟真实用户的行为,非常适合测试复杂的AI接口。本文将介绍如何使用Locust对DeepSeek服务进行全方位的性能与稳定性验证。

1. 为什么不能用ab/wrk?

ab (Apache Bench) 和 wrk 是Web服务器压测的神器,但在LLM场景下,它们有几个致命缺陷:

  1. 无法模拟流式响应 :它们只关注HTTP状态码和整体耗时,无法解析SSE流,无法统计 TTFT (Time To First Token) 这一关键指标。
  2. 请求内容静态 :大模型对输入长度极其敏感。处理10个Token和1000个Token的负载天差地别。ab 只能发送固定的Payload,无法模拟真实世界中长短不一的对话分布。
  3. 缺乏思考时间 :真实用户在发完一句话后,会阅读、思考、再发下一句。wrk 是无脑轰炸,这会导致并发压力虚高,无法反映真实的系统承载能力(Capacity)。

2. 模拟真实负载:Locustfile编写指南

我们需要模拟真实的业务流量分布。根据OpenAI的公开数据,典型的对话长度分布服从 泊松分布

  • 10%的用户只发短问题(<50 Token)。
  • 80%的用户进行中等长度对话(500 Token)。
  • 10%的用户上传长文档(>5k Token)。

创建一个 locustfile.py

python 复制代码
from locust import HttpUser, task, between, events
import json
import time
import random

class DeepSeekUser(HttpUser):
    # 模拟用户思考时间:1到5秒之间
    wait_time = between(1, 5)

    @task(10) # 权重10,短对话
    def chat_short(self):
        self.send_request(prompt_len=50, output_len=100)

    @task(1) # 权重1,长文档
    def chat_long(self):
        self.send_request(prompt_len=5000, output_len=500)

    def send_request(self, prompt_len, output_len):
        # 构造伪数据,实际测试中可以使用真实语料库
        prompt = "test " * prompt_len
        payload = {
            "prompt": prompt,
            "max_new_tokens": output_len,
            "temperature": 0.7,
            "stream": True # 开启流式
        }
        
        start_time = time.time()
        first_token_time = None
        token_count = 0
        
        # 使用catch_response手动处理结果
        with self.client.post("/generate", json=payload, stream=True, catch_response=True) as response:
            if response.status_code != 200:
                response.failure(f"Status code: {response.status_code}")
                return
            
            # 模拟接收流式响应
            try:
                for line in response.iter_lines():
                    if not line: continue
                    
                    # 记录首字时间
                    if not first_token_time:
                        first_token_time = time.time()
                        ttft = (first_token_time - start_time) * 1000
                        # 自定义上报TTFT指标
                        events.request.fire(
                            request_type="grpc",
                            name="TTFT",
                            response_time=ttft,
                            response_length=0
                        )
                    
                    token_count += 1
                
                total_time = time.time() - start_time
                # 计算生成速度
                tps = token_count / (total_time - (first_token_time - start_time))
                
            except Exception as e:
                response.failure(f"Stream error: {e}")

3. 核心指标解读与分析

运行压测命令:locust -f locustfile.py --host http://localhost:8000 --headless -u 50 -r 1

在控制台或Web UI中,我们需要重点关注以下指标,并学会透过数据看本质:

3.1 RPS vs TPS

  • RPS (Requests Per Second):对于长任务,RPS可能很低(比如0.5)。这不代表性能差,因为一个Request可能持续20秒。
  • TPS (Tokens Per Second) :这是衡量LLM服务吞吐量的黄金指标 。需要在服务端统计,或者像上面代码那样在Locust端估算。
    • 正常曲线:随着并发用户数增加,TPS应该线性增长,直到达到显存带宽瓶颈,然后趋于平稳。
    • 异常曲线 :如果并发增加,TPS反而下降,说明发生了严重的 资源争抢(如Python GIL锁竞争、Cache Thrashing)。

3.2 Latency: P99 vs Average

  • 平均延迟:毫无意义,千万别看。因为长短任务混杂,平均值会被长任务拉高,掩盖了短任务的性能问题。
  • P99 Latency :尾部延迟。如果P99飙升,说明系统内部出现了 排队(Queuing)
    • 排队原因:Dynamic Batching的等待队列满了,或者是KV Cache显存不足导致Swap。

3.3 Failure Rate

  • Timeout:Nginx或客户端设置的超时时间太短。
  • Connection Reset:服务端进程崩溃(OOM)。
  • 503 Service Unavailable:负载均衡器主动拒绝了请求(熔断)。

4. 稳定性测试(Soak Testing)

除了测极限性能(Stress Test),还需要测 长期稳定性。让Locust以中等负载(比如60%峰值)连续运行24小时。

重点观察对象

  1. 显存泄漏(Memory Leak)
    • 使用 npu-smi info 监控。显存占用应该在一定范围内波动。如果发现显存占用随时间呈现锯齿状上升,且低谷越来越高,说明有Tensor未释放。
  2. 僵尸进程
    • 检查 ps -ef | grep python。是否有处理完请求但未退出的孤儿进程。
  3. 温度墙(Thermal Throttling)
    • 长时间满载可能导致NPU温度过高(>80度),触发硬件降频。这会导致推理速度突然变慢。

5. 混沌测试(Chaos Testing)

进阶玩家还可以尝试"搞破坏",验证系统的高可用性(HA)。

  • 断网演练 :在压测过程中,模拟网络丢包(iptables -I INPUT -m statistic --mode random --probability 0.1 -j DROP),看客户端SDK是否能自动重连。
  • 杀节点:随机Kill掉一个推理Pod,看负载均衡器能否快速(<3秒)剔除故障节点,并将流量转移到健康节点。
  • 显存碎片化模拟 :发送大量长度极其怪异的请求(如 [1, 8192, 3, 4000]),通过极端分布测试PagedAttention的内存碎片整理能力。

6. 总结

压测不是为了生成一份漂亮的报告,而是为了 发现系统的崩溃点(Breaking Point)

  • 如果不测TTFT,你就不知道用户等待首字的焦虑。
  • 如果不测长文档并发,你就不知道显存什么时候会OOM。
  • 如果不做24小时稳定性测试,你就不知道内存泄漏会在深夜搞垮服务。

通过基于Locust的全方位实战验证,我们不仅能摸清DeepSeek服务的性能边界(Capacity Planning),还能提前暴露那些只在极端并发下才会出现的Race Condition和资源竞争Bug,确保上线即稳如磐石。

相关推荐
大模型任我行4 小时前
上海AI Lab:构建诊断型Agent守门员
人工智能·语言模型·自然语言处理·论文笔记
码农三叔4 小时前
(7-3-02)电机与执行器系统:驱动器开发与控制接口(2)实时通信总线设计+33自由度人形机器人的双信道EtherCAT主设备架构
人工智能·机器人·人形机器人
shangjian0074 小时前
AI-大语言模型LLM-概念术语-Causal LM
人工智能·语言模型·自然语言处理
才盛智能科技4 小时前
元K:自助KTV行业AI生态领航者
大数据·人工智能·物联网·自助ktv系统·才盛云自助ktv系统
shangjian0074 小时前
AI-大语言模型LLM-模型微调2-BitFit微调
人工智能·语言模型·自然语言处理
掘根4 小时前
【C++ AI大模型接入SDK项目】项目背景,项目介绍,环境准备
人工智能
相思难忘成疾4 小时前
华为HCIP:MPLS实验
网络·华为·智能路由器·hcip
玄同7654 小时前
LangChain v1.0 中间件深度解析:从 Callback 到 Middleware 的演进
人工智能·语言模型·自然语言处理·中间件·langchain·agent·智能体
小毅&Nora4 小时前
【人工智能】【大模型】从厨房到实验室:解密LLaMA架构如何重塑大模型世界
人工智能·架构·llama