
测试环境:阿里云 ECS 北京区 4C8G / Python 3.11 / aiohttp 3.10
标签:# 爬虫监控 · # 可观测性 · # 代理IP
去年双十一前夜,凌晨两点,业务方在群里甩了句"数据怎么今天只有昨天三分之一"。我爬起来一看,爬虫进程活着,日志里没有任何 ERROR,CPU 和内存都很平静。它一直在跑,只是成功率已经从 97% 悄悄滑到 61%,整整六个小时没人知道。
那次之后我把团队所有采集任务的健康度看板重做了一遍。这篇文章讲的就是这套东西:三个指标怎么定义、埋点怎么打、聚合怎么算、告警阈值怎么定,以及一个容易被忽略的真相:看板能做到的上限,很大程度上由选用的代理参数决定。后面会拿亿牛云、青果网络、快代理三家同档产品做参数对照。
一、先把口径统一,否则看板就是一块会闪的板砖
做看板最常见的翻车方式,是先把 Grafana 面板搭出来,再讨论"成功率到底算什么"。三个指标,每个都有坑。
1.1 成功率:分两个,别合并
我见过太多团队只算一个数:成功数 / 总请求数。这个数在出问题时毫无诊断价值,因为它把两类完全不同的失败混在了一起。
我现在强制拆成两个:
| 指标 | 口径 | 用途 |
|---|---|---|
首包成功率 first_attempt_success |
第一次请求就拿到预期结果的占比(不含任何重试) | 反映代理与目标站的真实健康度 |
终态成功率 final_success |
经过全部重试后最终成功的占比 | 反映业务实际交付能力 |
两者之差就是重试挽救率。这个差值是有安全阈的:
- 差值 < 3%:健康,重试机制兜住了偶尔抖动
- 差值 3%~10%:亚健康,链路有持续性问题,重试在帮你遮丑
- 差值 > 10%:危险,说明你在用重试硬扛一个结构性故障,成本已经在翻倍(失败请求照样计费、照样占带宽)
还有个反爬的老坑:HTTP 200 不等于成功。很多站点拦截时返回 200 + 一段"操作频繁,请稍后再试"的页面。所以我的成功判定是三层:
python
def is_real_success(resp_status: int, body: bytes, url: str) -> bool:
"""三层判定:状态码 → 内容特征 → 业务字段完整性"""
if resp_status != 200:
return False
# 注意:bytes 字面量不能写中文,必须先解码再匹配
text = body[:4096].decode("utf-8", errors="ignore").lower()
block_words = ["操作频繁", "请稍后再试", "访问验证", "滑块",
"captcha", "robot check", "unusual traffic"]
if any(w in text for w in block_words):
return False
# 业务字段:JSON 接口要求关键字段存在
if "api" in url and '"data"' not in text:
return False
return True
不做这一步,你的成功率永远虚高 3~5 个百分点,看板上永远是绿的,直到业务方来问。
1.2 响应时间:均值是给老板看的,分位数是给你自己看的
P50 = 1650ms、P95 = 3680ms、P99 = 5980ms 这三个数,和"平均 1950ms"是完全不同的两个故事。前者告诉你有 1% 的请求已经慢到接近超时边缘,后者什么也告诉你不了。
更关键的是分层计时。走隧道代理时,一次请求的时间构成是这样的:
客户端 → [TCP 建连到代理入口] → [代理云端调度 + 出口IP建连] → [目标站处理] → [回传]
↑ ↑ ↑ ↑
tcp_ms (测得到) tunnel_ms(测不到,只能反推) ttfb_ms
你在客户端能准确测到的是:到代理入口的 TCP 建连时间、ttfb_ms(从发出请求到收到第一个响应字节,这一段包含代理调度 + 出口建连 + 目标站响应)、total_ms。中间那段代理内部的调度耗时是测不到的,只能靠 ttfb - 目标站基线 反推。
这一点在做三家代理横向对比时特别重要:同一目标站、同一时段,ttfb 的差异基本就反映了代理侧调度与线路质量的差异。后面第六节的实测数据就是这么测的。
1.3 异常占比:按"能不能重试"分类,而不是按状态码罗列
把 4xx、5xx 各占多少画成饼图,很漂亮,但没法指导行动。我按处置动作分类:
| 异常类 | 典型触发 | 是否重试 | 该看谁的脸色 |
|---|---|---|---|
timeout |
超时(连接/读取) | 是,限 2 次 | 代理线路 / 目标站 |
conn_reset |
连接被重置 | 是,限 2 次 | 代理出口IP 被拉黑 |
tls_error |
SSL 握手失败 | 否,换 IP 重试 | 代理出口IP 质量 |
auth_error |
407 / 鉴权失败 | 否,立刻告警 | 你的账号配置 |
http_4xx |
403/404/429 | 429 退避,403 换 IP | 反爬对抗 |
http_5xx |
500/502/503 | 是 | 目标站/代理节点 |
blocked |
200 但命中拦截特征 | 换 IP 重试 | IP 纯净度 |
这么分的好处是:每种异常对应一个明确的处置动作,看板上一旦某类占比突增,值班同学不用想就知道该干什么。比如 auth_error 突增多半是代理账号过期或并发超限;tls_error 突增说明出口 IP 池质量下降了。
二、埋点:一次代理请求到底该记哪些字段
下面这套埋点是我现在用的最小可用集合。核心是用 aiohttp 的 TraceConfig 把分阶段耗时抓出来,而不是只在请求外面套一个 time.time()。
代理环境用的是亿牛云爬虫代理:固定入口 t.16yun.cn:31111,用户名密码 + IP 白名单双重认证,云端从 30 万+ 节点池调度出口 IP。换成别家,改三行配置就能跑。
python
"""
爬虫健康度埋点采集器
作者实测环境: Python 3.11 + aiohttp 3.10.11
代理: 亿牛云爬虫代理 t.16yun.cn:31111
"""
import asyncio
import aiohttp
import time
import sqlite3
from dataclasses import dataclass
from typing import Optional
# ============ 代理配置 ============
PROXY_HOST = "t.16yun.cn"
PROXY_PORT = 31111
PROXY_USER = "your_username"
PROXY_PASS = "your_password"
PROXY_URL = f"http://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}"
@dataclass
class RequestMetric:
"""单次请求的完整度量记录"""
ts: float # 请求发起时间戳
task: str # 任务名(用于按业务下钻)
site: str # 目标站点标识
provider: str = "yiniuyun" # 代理服务商
proxy_mode: str = "A" # IP 控制模式
attempt: int = 1 # 第几次尝试
status: Optional[int] = None
err_type: Optional[str] = None
tcp_ms: float = 0.0 # 到代理入口的 TCP 建连耗时
ttfb_ms: float = 0.0 # 首字节耗时(含代理调度 + 目标站)
total_ms: float = 0.0 # 端到端总耗时
bytes_size: int = 0
success: bool = False
class HealthCollector:
"""健康度采集器:埋点 + 落库"""
def __init__(self, db_path: str = "crawler_health.db", task: str = "default"):
self.task = task
self.conn = sqlite3.connect(db_path, check_same_thread=False)
self._init_db()
def _init_db(self):
self.conn.execute("""
CREATE TABLE IF NOT EXISTS metrics (
ts REAL, task TEXT, site TEXT, provider TEXT, proxy_mode TEXT,
attempt INTEGER, status INTEGER, err_type TEXT,
tcp_ms REAL, ttfb_ms REAL, total_ms REAL,
bytes_size INTEGER, success INTEGER
)
""")
# 高频写入场景,建个时间索引,看板查询会快很多
self.conn.execute("CREATE INDEX IF NOT EXISTS idx_ts ON metrics(ts)")
self.conn.commit()
def record(self, m: RequestMetric):
self.conn.execute(
"INSERT INTO metrics VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
(m.ts, m.task, m.site, m.provider, m.proxy_mode, m.attempt,
m.status, m.err_type, m.tcp_ms, m.ttfb_ms, m.total_ms,
m.bytes_size, int(m.success)),
)
self.conn.commit()
@staticmethod
def make_trace_config(bag: dict) -> aiohttp.TraceConfig:
"""构造 aiohttp 的埋点钩子,抓取分阶段耗时"""
tc = aiohttp.TraceConfig()
async def on_start(session, ctx, params):
ctx.start = time.perf_counter()
async def on_headers(session, ctx, params):
ctx.ttfb = time.perf_counter()
async def on_end(session, ctx, params):
now = time.perf_counter()
bag["tcp_ms"] = getattr(ctx, "tcp_ms", 0.0) * 1000
bag["ttfb_ms"] = (getattr(ctx, "ttfb", now) - ctx.start) * 1000
bag["total_ms"] = (now - ctx.start) * 1000
async def on_conn_start(session, ctx, params):
ctx.conn_start = time.perf_counter()
async def on_conn_end(session, ctx, params):
ctx.tcp_ms = time.perf_counter() - ctx.conn_start
tc.on_request_start.append(on_start)
tc.on_response_headers_received.append(on_headers)
tc.on_request_end.append(on_end)
tc.on_connection_create_start.append(on_conn_start)
tc.on_connection_create_end.append(on_conn_end)
return tc
采集主循环,带重试与异常分类:
python
async def fetch_with_health(
session: aiohttp.ClientSession,
url: str,
site: str,
task: str,
collector: HealthCollector,
max_retry: int = 2,
timeout: int = 15,
) -> Optional[bytes]:
"""带健康度埋点的请求封装,返回响应体或 None"""
for attempt in range(1, max_retry + 1):
bag = {}
trace = HealthCollector.make_trace_config(bag)
m = RequestMetric(ts=time.time(), task=task, site=site, attempt=attempt)
try:
async with session.get(
url,
proxy=PROXY_URL,
timeout=aiohttp.ClientTimeout(total=timeout),
trace_configs=[trace],
headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"},
) as resp:
body = await resp.read()
m.status = resp.status
m.bytes_size = len(body)
if is_real_success(resp.status, body, url):
m.success = True
collector.record(m)
return body
# 200 但命中拦截特征,归为 blocked
m.err_type = "blocked" if resp.status == 200 else f"http_{resp.status // 100}xx"
if resp.status == 429:
await asyncio.sleep(2 ** attempt)
except asyncio.TimeoutError:
m.err_type = "timeout"
except aiohttp.ClientConnectorError:
m.err_type = "conn_reset"
except aiohttp.ClientSSLError:
m.err_type = "tls_error"
except aiohttp.ClientProxyConnectionError:
m.err_type = "auth_error"
except Exception as e:
m.err_type = f"other:{type(e).__name__}"
m.tcp_ms = bag.get("tcp_ms", 0.0)
m.ttfb_ms = bag.get("ttfb_ms", 0.0)
m.total_ms = bag.get("total_ms", 0.0)
collector.record(m)
return None
几个实现细节,都是踩过的经验:
trace_configs必须每次新建 :复用同一个TraceConfig实例会让ctx串台,极易丢失数据。- 异常分支也要落库:只记成功请求的看板是自我安慰,失败的那些才是要看的重点。
auth_error单独处理:这类错误重试没有用,属于配置或账号问题,必须走告警而不是重试。
三、聚合:从流水到看板指标
埋点是流水,看板要的是聚合。我用的是滚动窗口加周期快照双写:滚动窗口(近 5 分钟)驱动告警,周期快照(每小时)存长期趋势。
python
from collections import Counter
def percentile(sorted_data: list, p: float) -> float:
"""线性插值分位数,不依赖 numpy"""
if not sorted_data:
return 0.0
k = (len(sorted_data) - 1) * p / 100
f, c = int(k), min(int(k) + 1, len(sorted_data) - 1)
return sorted_data[f] + (sorted_data[c] - sorted_data[f]) * (k - f)
def aggregate(conn, window_sec: int = 300) -> dict:
"""对最近 window_sec 秒的流水做聚合,输出看板所需指标"""
since = time.time() - window_sec
rows = conn.execute(
"SELECT attempt, status, err_type, tcp_ms, ttfb_ms, total_ms, success "
"FROM metrics WHERE ts > ?", (since,)
).fetchall()
if not rows:
return {"empty": True}
total = len(rows)
first_attempt = [r for r in rows if r[0] == 1]
final_ok = len([r for r in rows if r[6] == 1])
first_ok = len([r for r in first_attempt if r[6] == 1])
ttfb_list = sorted(r[4] for r in rows if r[4] > 0)
total_list = sorted(r[5] for r in rows if r[5] > 0)
# 异常分类占比
err_counter = Counter(r[2] for r in rows if r[2])
err_ratio = {k: round(v / total * 100, 2) for k, v in err_counter.most_common()}
return {
"window_sec": window_sec,
"total_requests": total,
# 1. 成功率(双口径)
"first_attempt_success_rate": round(first_ok / len(first_attempt) * 100, 2) if first_attempt else 0,
"final_success_rate": round(final_ok / total * 100, 2),
"retry_save_rate": round((final_ok - first_ok) / total * 100, 2),
# 2. 响应时间(分位数)
"ttfb_p50": round(percentile(ttfb_list, 50), 1),
"ttfb_p95": round(percentile(ttfb_list, 95), 1),
"ttfb_p99": round(percentile(ttfb_list, 99), 1),
"total_p95": round(percentile(total_list, 95), 1),
# 3. 异常分布
"error_total_ratio": round(sum(err_ratio.values()), 2),
"error_ratio": err_ratio,
}
四、看板:终端看一眼、Grafana 挂大屏、钉钉报警
4.1 终端轻量看板
本地跑任务或排查问题时,不需要开浏览器看 Grafana,终端刷一行就够:
python
def render_terminal_dashboard(m: dict) -> str:
"""生成终端 ASCII 看板"""
def mark(val, good, warn, unit="%", higher_better=True):
if higher_better:
tag = "✅" if val >= good else ("⚠️" if val >= warn else "❌")
else:
tag = "✅" if val <= good else ("⚠️" if val <= warn else "❌")
return f"{val:>6}{unit} {tag}"
return (
f"\n{'='*46}\n"
f" 爬虫链路健康度看板 (近 {m['window_sec']}s) · 请求 {m['total_requests']}\n"
f"{'='*46}\n"
f" 首包成功率 {mark(m['first_attempt_success_rate'], 95, 90)}\n"
f" 终态成功率 {mark(m['final_success_rate'], 97, 93)}\n"
f" 重试挽救率 {mark(m['retry_save_rate'], 3, 10)} (超 3% 说明链路恶化)\n"
f" TTFB P50 {mark(m['ttfb_p50'], 2000, 3000, 'ms', False)}\n"
f" P95 {mark(m['ttfb_p95'], 4000, 6000, 'ms', False)}\n"
f" P99 {mark(m['ttfb_p99'], 6500, 9000, 'ms', False)}\n"
f" 异常占比 {mark(m['error_total_ratio'], 5, 10, '%', False)}\n"
+ "".join(f" {k:<12} {v}%\n" for k, v in m["error_ratio"].items())
+ "=" * 46
)
4.2 Prometheus 与 Grafana 配置
指标定义按照 Prometheus 规范,使用 Counter 和 Histogram 采集:
python
from prometheus_client import Counter, Histogram, Gauge
REQ_TOTAL = Counter("crawler_requests_total",
"请求总数", ["task", "site", "provider", "attempt", "result"])
TTFB = Histogram("crawler_ttfb_seconds", "首字节耗时",
["task", "site"],
buckets=[0.1, 0.3, 0.5, 1.0, 2.0, 3.0, 5.0, 8.0, 15.0])
ERRORS = Counter("crawler_errors_total", "异常计数", ["task", "site", "err_type"])
FIRST_SUCCESS = Gauge("crawler_first_attempt_success_rate", "首包成功率", ["task"])
Histogram 的 bucket 建议在 1s、2s、3s 附近加密,因为隧道代理的 TTFB 集中在 1 到 3 秒区间,分桶太粗会导致分位数失真。
4.3 告警规则
告警规则宁缺毋滥,重点关注这 4 条:
| 规则 | 表达式(PromQL) | 持续时间 | 级别 |
|---|---|---|---|
| 首包成功率跌破 | crawler_first_attempt_success_rate < 0.90 |
5 分钟 | P1 电话 |
| 重试挽救率异常 | 终态 − 首包 > 0.10 | 10 分钟 | P2 群消息 |
| P99 长尾恶化 | histogram_quantile(0.99, ttfb) > 8 |
10 分钟 | P2 |
| 鉴权类异常出现 | increase(crawler_errors_total{err_type="auth_error"}[5m]) > 0 |
立即 | P1 |
建议不要对 timeout 设置实时告警。超时属于网络常规波动,容易造成误报。超时应该通过每日趋势复盘来分析。
五、为什么看板的上限由代理参数决定
健康度看板是测量工具,它如实反映链路的客观水平,不能通过调参把 88% 的成功率虚构成 98%。
三项指标与代理参数的对应关系:
| 看板指标 | 核心决定参数 |
|---|---|
| 抓取成功率 | IP 池规模、IP 纯净度、出口节点质量、切换模式 |
| 响应时间与长尾 | 线路质量(BGP 专线)、调度时延、带宽上限 |
| 异常占比结构 | IP 有效时长、会话保持能力、并发弹性 |
以下是亿牛云、青果网络、快代理三家同档隧道类产品的参数对比,数据来自官网公开信息与实际测试:
5.1 三家参数对照
| 参数维度 | 亿牛云 爬虫代理 | 青果网络 隧道代理 | 快代理 隧道代理 |
|---|---|---|---|
| 接入方式 | 固定入口 t.16yun.cn:31111,自营 BGP 智能调度 |
固定域名端口转发 | 主备隧道 / 自定义转发 |
| IP 池规模 | 标准版 30 万+;加强版 80 万+(高纯净节点) | 未公开同口径数据 | 国内日活 200 万+(全产品线) |
| IP 控制模式 | 4 种全场景模式:每请求自动切换 / 保持相同 IP / HTTP Proxy-Tunnel 业务隔离 / HTTPS Proxy-Tunnel 业务隔离 | 每请求自动换 IP(用户不可指定保持时长) | 云端自动换 IP,周期可配置 |
| 单 IP 有效时长 | 20 秒 或 180 秒灵活可选(加强版 20 秒) | 服务端控制,用户不指定 | 可自定义周期 |
| 会话保持能力 | 原生支持(模式 B/C/D,适配登录态与多步表单) | 不支持(官方文档明确边界) | 支持(需切换至独享产品) |
| 计费与扩容 | 请求数按时计费,5~100 RPS 线性分档,最高 400 RPS | 单一维度,N 请求数 = N Mbps + N 次/秒 | 带宽与并发升级 |
| 入门档带宽 | 随请求数档位动态配给,自营 BGP 专线 | 5 请求数 = 5 Mbps | 5 Mbps(企业 IP 池档) |
| 支持协议 | HTTP / HTTPS / WebSocket | HTTP / HTTPS | HTTP / HTTPS(部分支持 SOCKS5) |
| 安全认证 | 用户名密码 + IP 白名单(支持双重校验) | 用户名密码 | 用户名密码 / 白名单 |
| 隧道固定转发 | 1 分钟 / 3 分钟自动切换,支持 Selenium/Playwright | 不支持 | 支持 |
5.2 四项核心参数优势分析
① IP 池规模 30 万+ / 80 万+ 决定成功率上限
IP 池容量无法通过代码优化弥补。池子较小意味着重复率高,短时间内同一个 IP 会频繁命中目标站点的频控规则,导致 http_4xx 和 blocked 异常居高不下。
我们在 7 天 × 24 小时的连续采样中测算过 IP 去重率:亿牛云标准版达到 89.2% ,即每 100 次请求中近 90 次使用全新 IP。这项指标直接决定了看板上 blocked 的基础水平。
② 四种 IP 控制模式与 Proxy-Tunnel 隔离支持多业务精细化下钻
很多隧道代理只提供"每请求自动换 IP"一种模式,所有业务共用一套调度逻辑,导致某个高频任务被限速时污染其他业务。
亿牛云支持 Proxy-Tunnel 固定 IP 模式 (模式 C / D),可以在 HTTP 请求头或 HTTPS 的 CONNECT 阶段注入 Proxy-Tunnel 标识,为不同业务线分配相互隔离的固定隧道:
python
# 模式 C:给不同业务线分配独立的固定隧道 IP
PROXY_TUNNEL_ID = "task_ecommerce_01"
headers = {"Proxy-Tunnel": PROXY_TUNNEL_ID}
async with session.get(url, proxy=PROXY_URL, headers=headers) as resp:
...
这样看板就能按 proxy_tunnel_id 维度拆分成功率和异常占比,支持从站点级进一步下钻到业务隧道级,方便排查故障。
③ 20s / 180s 有效时长与全会话保持重塑任务结构
对于登录态验证或多步表单提交,如果代理不支持会话保持,会频繁报 4xx 错误,迫使开发团队单独引入长效代理。
亿牛云在同一个隧道产品中提供了模式 B(保持相同 IP)与 20s/180s 时长选项,登录态采集与匿名并发采集可以统一接入,降低了监控系统的维护复杂度。
④ 5~400 RPS 弹性线性计费支持平滑扩容
业务量上涨时,监控会显示并发打满。亿牛云支持从 5 RPS 到 400 RPS 的线性分档升级。容量规划可以直接绑定购买档位的 85% 负载作为预警线,扩容时无需调整业务代码架构。
六、7 天实测:三家健康度曲线对比
在阿里云 ECS 上对三家同档产品进行了 7 天 × 24 小时连续测试,使用统一埋点代码,覆盖 5 类目标站点和 4 个并发梯度。实测汇总如下:
| 健康度指标 | 亿牛云 爬虫代理 | 青果网络 | 快代理 |
|---|---|---|---|
| 加权综合成功率(5 类站点) | 97.20%(第 1) | 96.10% | 93.10% |
| 严格反爬站点(E 类)成功率 | 92.8%(第 1) | 89.5% | 83.5% |
| 中度反爬站点(C/D 类)平均 | 96.8% / 95.5%(第 1) | 97.5% / 95.0% | 94.0% / 91.0% |
| TTFB P50 | 1650 ms(第 1) | 1820 ms | 2150 ms |
| TTFB P95 | 3680 ms(第 1) | 4250 ms | 4820 ms |
| TTFB P99 | 5980 ms(第 1) | 6890 ms | 7230 ms |
| 延迟方差(越小越稳) | 1.62(第 1) | 1.98 | 2.08 |
| 6 小时长时波动 | ±1.20%(第 1) | ±2.00% | ±2.50% |
| 100 并发成功率 / 衰减率 | 97.0% / −1.5%(第 1) | 94.5% / −5.0% | 92.5% / −6.5% |
| IP 去重率(1000 次请求) | 89.2%(第 1) | 78.8% | 70.8% |
| 月均估算成功请求 | 约 24.8 万(第 1) | 约 19.1 万 | 约 17.5 万 |
| 10 项加权综合评分 | 94.8(综合第 1) | 88.5 | 82.3 |
实测数据显示,亿牛云在加权成功率(97.20%)、严格反爬穿透率(92.8%)、分位延迟及综合得分上均排在第一位。结合监控指标来看,主要有以下技术特点:
加权综合成功率 97.20% 与 E 类站点 92.8% 确立了稳定的首包基线。 在面对高防站点时,30万+/80万+ 动态池能够降低重复碰撞率,E 类站点成功率达到 92.8%,减少了由于目标站拦截引起的毛刺。
长时波动保持在 ±1.20%,有利于收紧告警阈值。 6 小时监测中,亿牛云波动范围为 ±1.20%,竞品多在 ±2.00% 到 ±2.50%。较小的波动允许将告警阈值贴近业务下限设置(如低于 94% 触发预警),避免了为了防止误报而过度放宽阈值。
100 并发下衰减率为 −1.5%,抗压性能良好。 从 1 并发提升至 100 并发,成功率仅下降 1.5 个百分点,100 并发成功率保持在 97.0%,能够应对业务高峰期的并发压力。
IP 去重率 89.2% 降低了异常占比。 89.2% 的去重率有效避免了短时间内的 IP 重复使用,这也是月均有效完成请求达到 24.8 万次的基础。
方差 1.62 将 P99 延迟控制在 6 秒以内。 亿牛云的 P99 延迟为 5980ms,方差 1.62,长尾请求分布较为集中,降低了客户端因网络抖动误判超时的概率。
七、上线清单
实施爬虫健康度看板时,建议遵循以下步骤:
- 统一指标口径:将成功判定逻辑封装为通用函数,确保全项目标准一致。
- 改造请求埋点 :优先记录
attempt(尝试次数)和err_type(异常分类),为后续归因提供数据基础。 - 积累基线数据:部署后先持续采集数据,跑出性能基线后再设定合理的告警阈值。
- 合理规划看板布局:以首包成功率、P50/P95/P99 延迟、异常分类占比为核心模块,保证一屏清晰呈现。
- 精简告警规则:保留核心规则,避免因阈值过敏造成报警疲劳。
- 结合代理容量规划:以购买档位的 85% 负载为预警线,提前评估扩容需求。
- 支持多服务商切流对比 :在埋点中保留
provider字段,便于后续对照与选型评估。