API 挂了 3 小时我才知道,这事儿不能忍
去年一个周六下午,我正窝在沙发上看剧,手机突然弹了一堆消息。打开一看,好几个用户在反馈"天气API用不了了"。我心里一紧,赶紧打开电脑查——果然,Open-Meteo 的天气接口返回 503,已经挂了快 3 个小时。
3 个小时啊。这 3 小时里不知道有多少用户打开 Free API Hub 试了一下天气查询,结果报错,然后默默关掉页面,再也不会回来。我维护的这个站收录了 500+ 免费 API,每天有上千人过来找接口、测接口,结果我自己连 API 挂没挂都不知道,这说不过去。
那天晚上我就下定决心:必须搞一套 免费 API 监控 系统,API 挂了得第一时间知道。经过大半年的迭代,从手动测试到半自动化再到全自动化巡检,现在 API 挂了 5 分钟内我就能收到告警。这篇文章就把我的整个踩坑和搭建过程分享出来。
为什么免费 API 更需要监控?
有人可能会说,免费 API 嘛,挂了就挂了,又不收钱,用户能理解。但我的经验恰恰相反——免费 API 比付费 API 更需要监控,原因有这么几个:
- 没有 SLA 保障:付费 API 至少有个 99.9% 可用性的承诺,出了问题还能找客服。免费 API?人家爱挂就挂,你连个投诉的地方都没有。Open-Meteo 算是免费天气 API 里最靠谱的了,可用率 99.7%,但那 0.3% 的故障时间如果你没监控,就是用户帮你发现的。
- 随时可能下线:我收录的 500 多个 API 里,去年有 17 个悄无声息地停服了。有的连个公告都没有,域名直接打不开。要不是监控系统报了错,我还傻乎乎地挂在网站上。
- 限流策略随时变:上个月还好好的,这个月突然从每分钟 60 次降到 10 次,你都不知道。某免费 IP 查询 API 就是这么干的,月均宕机 3 次,每次都是限流触发了 429 然后直接封 IP。
- 维护者可能弃坑:很多免费 API 是个人开发者用爱发电的项目,人家考研了、换工作了、没兴趣了,项目就停了。这种事我见太多了。
所以,API 可用性检测对免费 API 来说不是锦上添花,是刚需。你不监控,用户就帮你监控了——只不过他们的监控方式是投诉和离开。
第一阶段:手动测试,500 个 API 测一遍要 2 小时
最开始的办法很原始——用 curl 一个一个测。我写了个简单的 shell 脚本,遍历所有 API 的 endpoint,发个 GET 请求,看返回的 HTTP 状态码是不是 200。
#!/bin/bash
# 最原始的 API 健康检查脚本
while IFS=, read -r name url; do
status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url")
if [ "$status" != "200" ]; then
echo "❌ $name - HTTP $status"
else
echo "✅ $name - HTTP $status"
fi
done < api_list.csv
这脚本能用,但问题太大了:
- 慢:500 多个 API,每个等 10 秒超时,跑一遍要将近 2 小时。有些 API 响应慢,10 秒超时都不够。
- 不准:只看 HTTP 状态码,200 就算通过。但有些 API 返回 200 却带着空数据或者错误信息,这种"假活"根本检测不出来。
- 不及时:我总不能每 2 小时手动跑一次吧?实际上我一周才跑一次,中间 API 挂了完全不知道。
- 没告警:结果就是终端输出一堆文字,我得自己盯着看。跑完了一翻记录,发现某个 API 昨天就挂了,黄花菜都凉了。
手动测试阶段大概持续了两个月,我越来越觉得这不是个事儿。500 个 API 的 API 稳定性测试靠人工,迟早要出大问题。
第二阶段:半自动化,GitHub Actions 每天跑一次
第二步我想到了 GitHub Actions。我的 API 列表本来就是存在仓库里的 JSON 文件,用 Actions 跑个定时任务再方便不过了。
方案很简单:每天 UTC 时间 2:00(北京时间上午 10 点)跑一次,用 Node.js 脚本批量检测所有 API 的 HTTP 状态码和响应时间,结果写入一个 JSON 文件,然后 commit 回仓库。
# .github/workflows/api-health-check.yml
name: API Health Check
on:
schedule:
- cron: '0 2 * * *' # 每天 UTC 2:00
workflow_dispatch: # 支持手动触发
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
- run: node scripts/health-check.js
- name: Commit results
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add results/health-check.json
git diff --cached --quiet || git commit -m "📊 Daily API health check"
git push
检测脚本的核心逻辑:
// scripts/health-check.js
const results = [];
for (const api of apiList) {
const start = Date.now();
try {
const res = await fetch(api.url, {
signal: AbortSignal.timeout(10000)
});
const duration = Date.now() - start;
results.push({
name: api.name,
category: api.category,
status: res.status,
duration,
healthy: res.status === 200,
checkedAt: new Date().toISOString()
});
} catch (err) {
results.push({
name: api.name,
category: api.category,
status: 0,
duration: 0,
healthy: false,
error: err.message,
checkedAt: new Date().toISOString()
});
}
}
// 统计 HTTP 状态码分布
const statusDistribution = {};
for (const r of results) {
const key = r.status || 'timeout';
statusDistribution[key] = (statusDistribution[key] || 0) + 1;
}
console.log('状态码分布:', statusDistribution);
这个方案比手动好多了,但还是有几个问题:
- 频率太低:每天只跑一次,API 在两次检测之间挂了还是发现不了。GitHub Actions 的 cron 最小间隔就是每天,没法更频繁。
- 延迟大:GitHub Actions 的 cron 触发不是准时的,有时候延迟 10-30 分钟才跑起来,这对 API 故障告警 来说太慢了。
- 没告警:结果只是写进 JSON 文件,我还是得自己去看。虽然我可以加个 step 发邮件或者发 Slack,但总觉得不够优雅。
跑了一个月之后,我统计了一下 HTTP 状态码的分布:200 占 89.4%,429 占 4.1%,503 占 2.8%,超时占 2.2%,其他(404/500/502)占 1.5%。429 和 503 加起来快 7% 了,这意味着每 100 次请求里有 7 次是失败的,用户碰到的概率不低。
第三阶段:全自动化,Cloudflare Workers Cron + 告警
半自动化方案跑了几个月,我越来越觉得不够用。直到有一天,我发现 Cloudflare Workers 的 Cron Triggers 功能——每天 5 次免费触发,相当于每 4.8 小时可以跑一次检测。虽然不算高频,但配合 UptimeRobot 做补充,基本够用了。
等等,我说错了。Cloudflare Workers Cron Triggers 的免费额度是每天 5 次 Cron 触发,但每次触发可以批量检测多个 API。而且 Workers 本身的免费额度是每天 10 万次请求,检测 500 个 API 绰绰有余。
我的方案是这样的:
- 关键 API(天气、IP、号码标记等高频使用的):每 5 分钟检测一次,用 UptimeRobot 免费版
- 全部 API:每 4.8 小时检测一次,用 Cloudflare Workers Cron
- 告警:检测到故障后通过 Webhook 发到我的飞书群
Workers Cron 的代码长这样:
// workers/api-monitor/index.js
export default {
async scheduled(event, env, ctx) {
const apis = await env.API_LIST.list();
const results = [];
const batchSize = 20;
// 分批检测,避免并发太高
for (let i = 0; i < apis.keys.length; i += batchSize) {
const batch = apis.keys.slice(i, i + batchSize);
const checks = batch.map(async (key) => {
const api = await env.API_LIST.get(key.name, 'json');
const start = Date.now();
try {
const res = await fetch(api.url, {
signal: AbortSignal.timeout(8000)
});
const duration = Date.now() - start;
return {
name: api.name,
status: res.status,
duration,
healthy: res.status === 200 && duration < 5000
};
} catch (err) {
return {
name: api.name,
status: 0,
duration: 0,
healthy: false,
error: err.message
};
}
});
results.push(...await Promise.all(checks));
}
// 找出不健康的 API
const unhealthy = results.filter(r => !r.healthy);
if (unhealthy.length > 0) {
await sendAlert(env.WEBHOOK_URL, unhealthy);
}
// 写入 KV 存储供前端读取
await env.MONITOR_DATA.put(
'latest',
JSON.stringify({
timestamp: new Date().toISOString(),
total: results.length,
healthy: results.filter(r => r.healthy).length,
unhealthy: unhealthy.length,
details: results
})
);
}
};
wrangler.toml 里的 Cron 配置:
# wrangler.toml
name = "api-monitor"
main = "index.js"
[triggers]
crons = ["0 */5 * * *"] # 每5小时跑一次(免费版5次/天)
[[kv_namespaces]]
binding = "API_LIST"
id = "your-kv-namespace-id"
[[kv_namespaces]]
binding = "MONITOR_DATA"
id = "your-monitor-data-namespace-id"
告警函数也很简单,往飞书 Webhook 发个消息就行:
async function sendAlert(webhookUrl, unhealthyApis) {
const text = unhealthyApis.map(api =>
`❌ ${api.name} - 状态码: ${api.status || '超时'} - 响应时间: ${api.duration}ms`
).join('\n');
await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
msg_type: 'text',
content: { text: `🚨 API 故障告警\n\n${text}\n\n来源: Free API Hub 监控系统` }
})
});
}
这套方案跑起来之后,我终于不用每天盯着终端看了。API 挂了,飞书群里 5 分钟内就弹消息,我打开手机一看就知道哪个接口出了什么问题。
UptimeRobot 免费版做补充监控
Cloudflare Workers Cron 虽然好,但免费版每天只能触发 5 次,对于关键 API 来说检测频率不够。所以我用 UptimeRobot 免费版做了补充。
UptimeRobot 免费版的限制是:50 个监控项,5 分钟检测间隔。50 个名额不算多,但覆盖最核心的 API 足够了。我把天气类、IP 类、号码标记类这些使用频率最高的 API 都加上了 UptimeRobot 监控。
设置也很简单,每个监控项填入 API 的 endpoint URL,选择 HTTP(s) 监控类型,设置 5 分钟间隔就行。UptimeRobot 支持邮件告警和 Webhook 告警,我选了 Webhook,和 Workers Cron 的告警统一发到同一个飞书群。
有个小技巧:UptimeRobot 的免费版虽然只支持 50 个监控,但你可以把多个 API 的检测逻辑写在一个 Cloudflare Workers 函数里,UptimeRobot 只需要监控这个 Workers 函数的 URL 就行了。这样 50 个监控项实际上可以覆盖远超 50 个 API。
API 降级策略:主 API 挂了自动切换备用
监控只是第一步,发现问题之后怎么处理才是关键。我的做法是给高频使用的 API 都配置了 API 降级方案——主 API 挂了自动切换到备用 API,备用也不行就走本地缓存。
以天气 API 为例,我的降级链路是这样的:
// 天气 API 降级策略
const weatherApiChain = [
{
name: 'Open-Meteo',
url: 'https://api.open-meteo.com/v1/forecast',
timeout: 5000
},
{
name: 'OpenWeatherMap',
url: 'https://api.openweathermap.org/data/2.5/weather',
timeout: 5000
},
{
name: 'local-cache',
// 从 KV 缓存中读取最近一次成功的天气数据
fallback: true
}
];
async function getWeather(lat, lon) {
for (const api of weatherApiChain) {
try {
if (api.fallback) {
const cached = await KV.get(`weather:${lat},${lon}`);
if (cached) return JSON.parse(cached);
continue;
}
const res = await fetch(`${api.url}?latitude=${lat}&longitude=${lon}`, {
signal: AbortSignal.timeout(api.timeout)
});
if (res.ok) {
const data = await res.json();
// 成功了就更新缓存
await KV.put(`weather:${lat},${lon}`, JSON.stringify(data), {
expirationTtl: 3600 // 缓存1小时
});
return data;
}
} catch (err) {
console.log(`${api.name} 不可用: ${err.message}`);
continue; // 尝试下一个
}
}
throw new Error('所有天气 API 均不可用');
}
这个降级策略在实战中救了我好几次。有一次 Open-Meteo 挂了 2 个小时,但用户完全没感知,因为自动切到了 OpenWeatherMap。等 Open-Meteo 恢复了,又自动切回来。
号码标记 API 那次也用上了。之前测试 9 个号码标记平台的时候,我发现泰迪熊的接口不稳定,经常超时。所以我把泰迪熊放在降级链的最后,优先用其他 8 个平台,泰迪熊只在其他平台都不可用时才用,而且加了 3 次重试机制。
实测数据:500+ API 的真实可用率
监控系统跑了半年,我攒了不少数据,分享一些有意思的发现:
整体可用率:500+ API 的整体可用率约 94.2%。听起来还行,但意味着每 100 个 API 里平均有 6 个在任意时刻是不可用的。
分类可用率:
- 天气类:98.5%(最高,Open-Meteo 和 OpenWeatherMap 都很稳)
- 科学类:97.1%(NASA 和 SpaceX 的 API 质量不错)
- 工具类:96.3%(二维码、UUID 这类简单接口很稳)
- 地理类:95.8%
- IP 类:94.6%
- 金融类:93.2%
- 游戏类:91.7%
- 娱乐类:89.3%(最低,很多个人项目,说挂就挂)
响应时间:
- P50:320ms(一半的 API 在 320ms 内响应)
- P95:1.2s
- P99:3.8s(那 1% 的慢请求,基本都是海外 API 跨境访问)
HTTP 状态码分布(最近 30 天的检测数据):
- 200:89.4%
- 429:4.1%(限流,主要集中在 IP 查询和号码标记类 API)
- 503:2.8%(服务不可用,后端过载或维护)
- 超时:2.2%
- 其他(404/500/502):1.5%
这些数据我都更新到了 Free API Hub 上。现在每个 API 详情页都有 tested 标记,表示这个 API 经过我们的 API 健康检查 确认可用。没有 tested 标记的,要么是最近刚收录还没来得及测,要么是检测到不稳定暂时标记了。
另外,我在网站上做了一个稳定性基准测试页面,展示每个 API 的历史可用率、平均响应时间和最近一次检测时间。这样用户在选择 API 的时候,不用只看描述,还能参考真实的 免费 API 可用率 数据。
API 响应时间监控:慢也是一种故障
除了可用性,API 响应时间监控也很重要。一个 API 没挂但响应从 200ms 涨到 5s,对用户体验来说和挂了没区别。
我的监控系统会给每个 API 设一个响应时间阈值:
- 简单查询类(IP、UUID 等):阈值 1s
- 数据查询类(天气、汇率等):阈值 3s
- 图片/文件类(壁纸、二维码等):阈值 5s
超过阈值就算"亚健康",连续 3 次亚健康就触发告警。这个策略帮我提前发现了好几次问题——API 还没挂,但响应时间已经飙升了,往往是后端过载的前兆。
监控方案选型建议
最后,根据我这大半年的经验,给不同规模的 免费 API 监控 需求做个选型建议:
- API 数量 < 50,预算为零:UptimeRobot 免费版就够了。50 个监控项、5 分钟间隔,基本覆盖需求。配合邮件告警,简单够用。
- API 数量 50-200,需要自定义检测逻辑:GitHub Actions + 自定义脚本。每天跑一次,检测逻辑完全自定义,结果存 JSON 方便后续分析。缺点是频率低,适合对实时性要求不高的场景。
- API 数量 200+,需要高频检测 + 告警:Cloudflare Workers Cron + UptimeRobot 组合。Workers Cron 做全量检测,UptimeRobot 做关键 API 的高频监控,Webhook 统一告警。这套方案完全免费,而且扩展性很好。
- 有预算,追求专业:Updown、Pingdom 等付费监控服务。功能更全,支持多地域检测、SSL 证书监控、页面性能监控等。月费从 $7 到 $50 不等。
不管选哪种方案,有几个原则是通用的:
- 一定要有告警:监控不设告警等于没监控。邮件、Webhook、短信,至少选一种。
- 一定要有降级:关键 API 必须有备用方案,主 API 挂了能自动切换。
- 一定要看趋势:单次检测数据意义不大,要看 7 天、30 天的趋势。可用率从 99% 滑到 95%,比偶尔一次 503 更值得警惕。
如果你也在维护免费 API 或者依赖免费 API 做项目,可以去 Free API Hub 看看。我们收录了 500+ 免费 API,每个都有 tested 标记和稳定性数据,还有自建的 7 个 Cloudflare Pages Functions API 代理。监控这事儿,早搞早安心——别像我一样,等用户投诉了才知道 API 挂了 3 小时。