asyncio+aiohttp 实测:单机如何稳住 150 并发、上探 800?
稳态并发在 100–150 时仍为亚秒级;并发 200 时 P95 ≈ 790ms,整体可接受。并发 500/800 时长尾显著上升,且出现少量客户端异常(失败率>0)。
·
asyncio+aiohttp 实测:单机如何稳住 150 并发、上探 800?
压测记录(脱敏版)
- 接口路径:
GET /api/mp/article/detail?articleUid=<redacted> - 鉴权:
Authorization: <redacted>(统一使用脱敏 Token 集) - 环境: 某 SIT 网络环境(Host/IP 已脱敏)
- 工具与方法:
- 客户端:asyncio + aiohttp(Keep-Alive),单进程事件循环
- 令牌:120 个用户 Token 轮询(脱敏)
- 场景:基线、爬坡(50→200),高并发(500/800)
- 指标口径:错误率=HTTP错误/总请求;失败率=客户端异常(连接/超时)/总请求;RPS=总请求/阶段耗时
结果摘要
- 稳态并发在 100–150 时仍为亚秒级;并发 200 时 P95 ≈ 790ms,整体可接受。
- 并发 500/800 时长尾显著上升,且出现少量客户端异常(失败率>0)。
指标明细
- 字段:总请求数、每秒请求数、错误率、失败率、平均响应时间ms、最小响应时间ms、最大响应时间ms、90th响应时间ms、95th响应时间ms、99th响应时间ms
| 场景 | 并发 | 总请求数 | 每秒请求数 | 错误率 | 失败率 | 平均(ms) | 最小(ms) | 最大(ms) | P90(ms) | P95(ms) | P99(ms) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 基线 | 20 | 400 | 254.69 | 0.0000 | 0.0000 | 74.07 | 16.17 | 621.98 | 137.25 | 168.26 | 296.30 |
| 爬坡-1 | 50 | 1,250 | 294.20 | 0.0000 | 0.0000 | 164.53 | 27.34 | 904.63 | 249.62 | 286.85 | 406.64 |
| 爬坡-2 | 100 | 2,500 | 282.53 | 0.0000 | 0.0000 | 345.40 | 31.17 | 1,010.93 | 442.46 | 503.42 | 793.78 |
| 爬坡-3 | 150 | 3,750 | 301.03 | 0.0000 | 0.0000 | 486.85 | 155.63 | 1,530.12 | 537.52 | 596.96 | 1,259.74 |
| 爬坡-4 | 200 | 5,000 | 296.42 | 0.0000 | 0.0000 | 660.44 | 69.68 | 1,979.16 | 741.07 | 790.10 | 1,577.33 |
| 高并发 | 500 | 15,000 | 262.87 | 0.0000 | 1.1133% | 1,871.58 | 15.16 | 10,930.84 | 1,854.54 | 2,877.70 | 10,143.01 |
| 高并发(复测) | 800 | 24,000 | 258.37 | 0.0000 | 0.0542% | 3,054.24 | 506.22 | 10,946.27 | 3,578.24 | 3,954.73 | 6,589.38 |
| 高并发(复测2) | 800 | 24,000 | 273.90 | 0.0000 | 2.0750% | 2,879.97 | 15.80 | 10,969.91 | 3,223.63 | 5,056.36 | 10,127.88 |
结论与建议
- 建议“稳定并发”设为 ≈150;并发 200 亦可,但长尾略升高。
- 并发 ≥ 500 时出现非零失败率与较大长尾,若需更高并发,建议:
- 采用预热+分阶段爬坡,并开启/优化连接复用;如可能,升级到 HTTP/2 多路复用以降低连接数。
- 与服务端/网关确认限流与排队策略,优化队列与线程/连接池;关注下游依赖瓶颈。
- 在网络路径存在代理/NAT 时,建议受控 RPS 压测,避免客户端过载引起的排队放大。
数据合规与脱敏说明
- 已隐藏 Host、IP 与 Token,文中仅展示相对路径与示例参数。
- 所有报告中涉及
Authorization、域名与账号信息均已脱敏处理。
代码与说明(脱敏示例)
- 代码位置(本地工程):
- Python 压测主脚本:
scripts/aiohttp_load_test.py - 运行脚本:
scripts/run_aiohttp_load_test.sh
- Python 压测主脚本:
- 运行依赖:
- Python 3.9+,
pip install aiohttp uvloop
- Python 3.9+,
- 运行示例(固定并发与爬坡):
# 固定并发
CONCURRENCY=200 TOTAL=10000 REPORT_DIR=./reports \
python3 scripts/aiohttp_load_test.py
# 爬坡(每步 30s,从 50→200)
RAMP=1 RAMP_START=50 RAMP_STEPS=4 RAMP_INC=50 STEP_DURATION=30 \
REPORT_DIR=./reports \
bash scripts/run_aiohttp_load_test.sh
脱敏版 Python 代码(核心片段)
说明:默认 API_URL 中的 Host 已脱敏,请替换为你的环境;脚本会从 CSV(首行为表头)读取 120 个 Token 并轮询使用。
#!/usr/bin/env python3
import asyncio, csv, json, os, time
from dataclasses import dataclass
from statistics import mean
from typing import List, Optional, Dict
try:
import uvloop # 可选:更高性能事件循环
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
except Exception:
pass
import aiohttp
def read_tokens(csv_path: str) -> List[str]:
tokens: List[str] = []
with open(csv_path, 'r', encoding='utf-8') as f:
rows = [row for row in csv.reader(f) if any(col.strip() for col in row)]
if not rows:
return []
header_like = any('auth' in c.lower() or 'token' in c.lower() for c in rows[0])
for row in rows[1 if header_like else 0:]:
if row and row[0].strip():
tokens.append(row[0].strip())
return tokens
def percentile(values: List[float], p: float) -> float:
if not values: return 0.0
a = sorted(values); k = (len(a) - 1) * (p / 100.0)
f = int(k); c = min(f + 1, len(a) - 1)
return float(a[f] * (c - k) + a[c] * (k - f))
@dataclass
class Result:
ok: bool
status: int
latency_ms: float
err: Optional[str]
async def fetch(session: aiohttp.ClientSession, url: str, token: str, insecure: bool) -> Result:
start = time.perf_counter()
try:
async with session.get(url, headers={'Authorization': token, 'Accept': 'application/json'},
ssl=False if insecure else None) as resp:
await resp.read()
return Result(resp.status == 200, resp.status, (time.perf_counter() - start) * 1000.0, None)
except Exception as e:
return Result(False, 0, (time.perf_counter() - start) * 1000.0, str(e))
async def run_fixed_load(api_url: str, article_uid: str, tokens: List[str],
concurrency: int, total: int, timeout_s: float,
insecure: bool, dns_ttl: int) -> Dict:
started = time.perf_counter()
connector = aiohttp.TCPConnector(limit=concurrency, limit_per_host=concurrency,
ttl_dns_cache=dns_ttl, enable_cleanup_closed=True,
force_close=False, keepalive_timeout=30)
timeout = aiohttp.ClientTimeout(total=timeout_s)
url = f"{api_url}?articleUid={article_uid}"
sem = asyncio.Semaphore(concurrency)
lat: List[float] = []; succ = 0; fail = 0; codes: Dict[str, int] = {}
async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
async def worker(i: int):
nonlocal succ, fail
async with sem:
r = await fetch(session, url, tokens[i % len(tokens)], insecure)
lat.append(r.latency_ms); codes[str(r.status)] = codes.get(str(r.status), 0) + 1
succ += 1 if r.ok else 0; fail += 0 if r.ok else 1
await asyncio.gather(*[asyncio.create_task(worker(i)) for i in range(total)])
elapsed = max(1e-9, time.perf_counter() - started)
exc = codes.get("0", 0); http_err = max(0, fail - exc)
return {
'url': url, 'concurrency': concurrency, 'total_requests': total,
'successes': succ, 'failures': fail, 'error_rate': round(http_err/total, 6),
'failure_rate': round(exc/total, 6), 'rps': total/elapsed, 'status_count': codes,
'latency_ms': {'avg': mean(lat) if lat else 0, 'min': min(lat) if lat else 0,
'p50': percentile(lat, 50), 'p90': percentile(lat, 90),
'p95': percentile(lat, 95), 'p99': percentile(lat, 99),
'max': max(lat) if lat else 0}
}
关键实现说明
- Token 读取:支持首行表头并自动跳过空行,保证 CSV 兼容性。
- 连接器配置:
limit/limit_per_host设为并发值;keepalive_timeout=30维持长连;ttl_dns_cache缓存 DNS 降低开销。 - 请求与断言:最小化客户端逻辑,仅检查 HTTP 200 并完整读取响应;异常归入状态码
0。 - 度量口径:
error_rate: 非 2xx 的 HTTP 错误比率(此接口以 200 为准)failure_rate: 客户端异常(连接错误/超时等)比率rps:总请求数 / 实际阶段耗时- 延迟分位:
p50/p90/p95/p99与avg/min/max
- 爬坡模式:通过阶段性并发与时长组合,避免队列无限膨胀,便于定位拐点。
- 报告输出:支持 JSON/CSV;CSV 字段即“指标明细”表中的各列。
脱敏提示:示例代码默认
API_URL为https://<redacted-host>/api/mp/article/detail,请在本地替换为你的环境域名;Token 文件请使用脱敏数据或受控环境凭证。
脚本设计思路
- 目标与原则:在单机条件下以最低开销复现真实业务访问模式,快速定位稳定并发与性能拐点;保持脚本简洁、可读、可扩展。
- 输入与配置:
- Token 管理:从 CSV 读取,自动跳过表头/空行,轮询分配到请求,避免单 Token 限流与缓存偏置。
- 参数化:支持
API_URL、ARTICLE_UID、并发/总量/超时、DNS TTL、是否忽略 TLS 校验等,既可命令行也可环境变量。 - 场景模式:固定负载(并发×总量)与爬坡模式(起始并发、步数、步进、步长时间)。
- 并发与连接策略:
- 事件循环:优先使用 uvloop(如可用),降低调度与系统调用成本。
- 连接复用:aiohttp 单
ClientSession+TCPConnector,设置limit/limit_per_host=并发、keepalive_timeout=30s,充分使用 Keep‑Alive。 - DNS 缓存:
ttl_dns_cache降低 DNS 解析频率,稳定长测表现。 - 限速与排队:通过信号量与任务批量创建控制队列规模,爬坡模式防止瞬时爆量导致拥塞。
- 请求与容错:
- 轻断言:以 HTTP 200 为成功标准,完整读取响应避免半关闭引发误判。
- 异常分类:将连接/超时等客户端异常计入状态码 “0”,与服务端错误(非 200)分离,便于分别优化。
- 最小化开销:禁用多余日志,计算与聚合在内存中完成,降低对结果的干扰。
- 度量与报告:
- 时延口径:收集
avg/min/max与p50/p90/p95/p99,覆盖“中心趋势+长尾”。 - 质量口径:区分
error_rate(HTTP 错误)与failure_rate(客户端异常),定位瓶颈侧(服务端/客户端/网络)。 - RPS 计算:以阶段实际耗时计算真实吞吐(总请求/阶段耗时)。
- 落盘:输出 JSON(全量字段)与 CSV(概览字段),方便二次分析与平台展示。
- 时延口径:收集
- 可扩展性:
- 多接口/多参数:可扩展为从用例集/CSV 读取不同路径与参数,模拟混合流量。
- 采样保存:为慢请求/错误请求保留样本(headers/片段化 body),辅助链路排障。
- 协议升级:按需接入 HTTP/2 客户端或受控 RPS(令牌桶)模式,验证限流与排队策略。
- 安全与合规:
- 脱敏输出:屏蔽 Host、IP 与 Token,不在报告中写入敏感字段。
- 资源保护:默认分阶段爬坡与短时预热,避免对共享环境造成冲击。
更多推荐


所有评论(0)