惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
爱范儿
爱范儿
人人都是产品经理
人人都是产品经理
博客园 - 司徒正美
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
量子位
罗磊的独立博客
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
小众软件
小众软件
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
大猫的无限游戏
大猫的无限游戏
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
Simon Willison's Weblog
Simon Willison's Weblog
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cyber Attacks, Cyber Crime and Cyber Security
博客园_首页
博客园 - 叶小钗
V
Vulnerabilities – Threatpost
T
The Exploit Database - CXSecurity.com
T
Tailwind CSS Blog
IT之家
IT之家
博客园 - 聂微东
Spread Privacy
Spread Privacy
V2EX - 技术
V2EX - 技术
S
Security Affairs
宝玉的分享
宝玉的分享
V
V2EX
C
Cisco Blogs
博客园 - Franky
美团技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell
月光博客
月光博客
S
Securelist
J
Java Code Geeks
Webroot Blog
Webroot Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Proofpoint News Feed
Last Week in AI
Last Week in AI
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
WordPress大学
WordPress大学
W
WeLiveSecurity
T
Threatpost
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志

竹林里有冰的博客

Nuxt SSG 博客的尾斜杠到底怎么加? | 竹林里有冰的博客 小米 Xiaomi Book Pro 14 (Ultra X7) Linux 兼容性实测 | 竹林里有冰的博客 国内(大陆)版小米 FCM 熄屏断连:Rootless 环境下的尝试与可能的解决方案 | 竹林里有冰的博客 我没法访问 dl.google.com —— 记一次 TUN 下的网络 debug | 竹林里有冰的博客 Vercel 的缓存控制,你注意过吗? | 竹林里有冰的博客 小记 —— Caddy 在 Layer 4 上的流量代理实践 | 竹林里有冰的博客 你的域名后缀拖慢你的网站速度了嘛?——再谈 DNS 冷启动 | 竹林里有冰的博客 DNS 冷启动:小型站点的“西西弗斯之石” | 竹林里有冰的博客 HTTP/2 Server Push 已事实性“死亡”,我很怀念它 | 竹林里有冰的博客 Nuxt Content v3 中数组字段的筛选困境与性能优化 | 竹林里有冰的博客 后 OCSP 时代,浏览器如何应对证书吊销新挑战 | 竹林里有冰的博客 初试 Github Action Self-hosted Runner,想说爱你不容易 | 竹林里有冰的博客 DNS 解析延迟毁了我的图床优化 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(下):告别 DOM 操作,拥抱 AST 与函数式渲染 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(上):从暴力刷新、分块更新到 Morphdom 的华丽变身 | 竹林里有冰的博客 node-sass 迁移至 dart-sass 踩坑实录 | 竹林里有冰的博客 前端中的量子力学——一打开 F12 就消失的 Bug | 竹林里有冰的博客 2025 年,如何为 web 页面上展示的视频选择合适的压缩算法? | 竹林里有冰的博客 el-image 和 el-table 怎么就打架了?Stacking Context 是什么? | 竹林里有冰的博客 2025年,前端如何使用 JS 将文本复制到剪切板? | 竹林里有冰的博客 ssh 拯救世界——通过 ssh 隧道在内网服务器执行 APT 更新 | 竹林里有冰的博客 Cudy TR3000 吃鹅(daed)记 | 竹林里有冰的博客 使用 Cloudflare Workers 监控 Fedora Copr 构建状态 | 竹林里有冰的博客 构建部署在 Cloudflare Workers 上的 TG Bot | 竹林里有冰的博客 2024年,Firefox 是唯一还在坚持执行在线的 SSL 证书吊销状态检查的主流浏览器 | 竹林里有冰的博客 小爱课程表适配不完全指北——以 ZJUT 本科正方教务系统为例 | 竹林里有冰的博客 将博客从 waline v2 更新到 waline v3 | 竹林里有冰的博客 给家里云装上 Fedora 41 KDE 后,我是如何配置的 | 竹林里有冰的博客 为 Hexo 添加 follow 认证 | 竹林里有冰的博客 使用 GPT 对 waline 的评论进行审查 | 竹林里有冰的博客 基于 JavaScript 的 Hexo Fluid 主题 banner 随机背景图实现 | 竹林里有冰的博客 使用向日葵智能插座 C2 用电记录推算宿舍上次烧水时间 | 竹林里有冰的博客 使用 Caddy 反向代理 dockerhub 需要几步? | 竹林里有冰的博客 将 Rustdesk 中继服务从 Arch Linux 迁移至 Debian | 竹林里有冰的博客 自建图床小记五——费用 | 竹林里有冰的博客 自建图床小记四——上传脚本编写与图片迁移 | 竹林里有冰的博客 自建图床小记三—— SSL 证书的自动更新与部署 | 竹林里有冰的博客 自建图床小记二——使用 Workers 为 R2 构建 Restful API | 竹林里有冰的博客 自建图床小记一——图床架构与 DNS 解析 | 竹林里有冰的博客 在 Linux 下使用 mitmproxy 抓取安卓手机上的 HTTPS 流量 | 竹林里有冰的博客 为中柏 N100 小主机开启来电自启 | 竹林里有冰的博客 我的博客被完整地反向代理,并自动翻译成了繁体中文 | 竹林里有冰的博客 尝试体验 Fedora COPR 中的 allow SSH 功能 | 竹林里有冰的博客 在 Arch Linux 下配置使用 HP Laser 103w 打印机无线打印 | 竹林里有冰的博客 使用动态公网 ip + ddns 实现 rustdesk 的 ip 直连 | 竹林里有冰的博客 使用 Windows 虚拟机运行虚拟专用网客户端为 Linux 提供内网环境 | 竹林里有冰的博客 以 Archlinux 中 makepkg 的方式打开 rpmbuild | 竹林里有冰的博客 使用 Github Action 更新用于 rpm 打包的 spec 文件 | 竹林里有冰的博客 使用 Python 生成甘特图(Gantt Chart) | 竹林里有冰的博客 uniapp 中的图片预加载 | 竹林里有冰的博客 小记 - 尝试拼凑出 apt 仓库中的 deb 包下载地址 | 竹林里有冰的博客 在 Linux 下使用 mitmproxy 抓取 HTTPS 流量 | 竹林里有冰的博客 如何使用 docker 部署 onemanager | 竹林里有冰的博客 crontab 中简单的@语法糖 | 竹林里有冰的博客 备份 umami 数据库,并使用 TG Bot 保存 dump 文件 | 竹林里有冰的博客 在 JavaScript 中,箭头函数中的 this 指针到底指向哪里? | 竹林里有冰的博客 结合 Vue.js 与 php 完成的 web 期末大作业,讲讲前后端分离站点开发与部署中可能遇到的 CORS 跨域问题 | 竹林里有冰的博客 vuejs、php、caddy 与 docker —— web 期末大作业上云部署 | 竹林里有冰的博客 【翻译】使用 PHP 构建简单的 REST API | 竹林里有冰的博客 在 Hexo Fluid 主题中使用霞鹜文楷 | 竹林里有冰的博客 【翻译】GLWTPL——祝你好运开源许可证 | 竹林里有冰的博客 通过巴法云将向日葵智能插座接入米家,实现小爱同学远程控制 | 竹林里有冰的博客 使用 Root 后的安卓手机获取向日葵智能插座 C2 的开关 api | 竹林里有冰的博客 创建 b23.tv 追踪参数移除 bot | 竹林里有冰的博客 jinja2 中如何优雅地实现换行 | 竹林里有冰的博客 手动指定 python-selenium 的 driver path 以解决在中国大陆网络环境下启动卡住的问题 | 竹林里有冰的博客 从零开始的静态网页部署(到个人云服务器) | 竹林里有冰的博客 在运行OpenWRT的N1盒子上部署 QQBot | 竹林里有冰的博客 在浙工大宿舍使用路由器连接移动网络(校园网) | 竹林里有冰的博客 为红米 Redmi AC2100 路由器刷入 Padavan | 竹林里有冰的博客 Azure 教育订阅申请时遇到的麻烦 | 竹林里有冰的博客 执行 repo sync 后将 git-lfs 中的资源文件 checkout | 竹林里有冰的博客 隐式转发——骚套路建站方案 | 竹林里有冰的博客 在 vps 上配合 caddy 部署 siteproxy | 竹林里有冰的博客 onedrive(by abraunegg) —— 一个 Linux 下的开源 OneDrive 客户端(cli) | 竹林里有冰的博客 【翻译】关于2022年11月的事件的一些话[Z-Library] | 竹林里有冰的博客 【已过期】使用 vercel+supabase 免费部署 umami | 竹林里有冰的博客 我的博客部署方案 | 竹林里有冰的博客 使用 VirtScreen 将 Pad 作为副屏 | 竹林里有冰的博客 在 Archlinux 下使用 l2tp 协议连接校园网 | 竹林里有冰的博客 为 Element 添加自己喜欢的贴纸 | 竹林里有冰的博客 nodejs16:是我配不上 openssl 3 咯? | 竹林里有冰的博客 如何拯救失声的 hollywood | 竹林里有冰的博客 处理 fcitx5 的文字候选框在 tg 客户端上闪烁的问题 | 竹林里有冰的博客 使用caddy反向代理维基百科中文站点 | 竹林里有冰的博客 创建一个本地的 Fedora 镜像源 | 竹林里有冰的博客 好软推荐——FastOCR | 竹林里有冰的博客 抛弃PicGo,直接使用curl将图片上传到LskyPro | 竹林里有冰的博客 使用 Github Action 跑 rpmbuild | 竹林里有冰的博客 如何打出一个「-git」的rpm包 | 竹林里有冰的博客 雪藏在开源镜像站点中的那些常用却不为人知的软件 | 竹林里有冰的博客 在Fedora搭建jekyll环境——dnf module | 竹林里有冰的博客 pacman更新时遇到「GPGME 错误:无数据」 | 竹林里有冰的博客 PicUploader使用系列(二)——为KDE的dolphin添加右键快捷菜单 | 竹林里有冰的博客 PicUploader使用系列(一)——在Archlinux上使用Caddy部署PicUploader | 竹林里有冰的博客 使用AUR(Helper)安装软件时究竟发生了什么?对于常见的构建错误如何解决? | 竹林里有冰的博客 下载一份openharmony的源码 | 竹林里有冰的博客 在Windows与Linux双系统下共享蓝牙鼠标 | 竹林里有冰的博客 我在Archlinux上的常用软件 | 竹林里有冰的博客 安卓解包笔记 | 竹林里有冰的博客
基于 Cloudflare Workers 实现的在线服务状态检测告警系统 | 竹林里有冰的博客
竹林里有冰 · 2025-01-18 · via 竹林里有冰的博客

起因#

受一些客观因素的影响,微精弘前一阵子针对学校教务系统的数据爬取服务状态出现了非常不稳定的状态,而后端在设计初并没有考虑到异常告警机制,恰逢现任员工都身陷期末周的痛苦之中,我这种计院 Lite 专业的精弘老人就打算实现一个针对「微精弘主后端 <-> funnel 爬虫服务 <-> 教务系统」这一条链路的告警机制。旨在短期内(即期末周结束之前)填补微精弘的后端服务告警机制的空白,让运维人员能够及时收到并处理排查后端网络链路的异常情况,尽最大努力保证服务在线率,保障工大本科生在期末周内使用体验。

需求分析#

微精弘的技术架构图微精弘的技术架构图

如果你不知道微精弘的具体架构实现,这里有一篇由前技术总监提笔并由现任技术总监完善的架构杂谈「微精弘 | 架构杂谈」,原文最初发表于前者的博客

基于上述客观条件以及我个人在服务监控告警领域近乎为 0 的经验,我一拍脑袋提出了以下几点需求:

  1. 稳定性。告警服务本身必须要比我们的主后端更加稳定,这是告警服务的基础。
  2. 针对现有服务的侵入性低。告警服务不能影响到现有服务,最好能够完全分离开来,不应当部署在同一台服务器上。
  3. 开发快速。整个服务需要尽快落地,因为现有的服务在一周内出现了三次故障,且由于主动的监控告警机制的缺失,我们每次都要等服务 down 机的两小时后才意识到服务挂掉了,如果真在考试周这个使用高峰期内再出现这样的故障不容允许的(用户需要查询考场信息)。
  4. 尽可能低的运维成本。没什么好解释的,谁也不希望一个告警服务占用太多的运维成本,无论是人力上的还是资源上的。

技术选型#

结合我已有的经验,我选用了 Cloudflare Worker 来完成这个任务。Cloudflare Workers 本身是支持 cron job 的,能够以分钟级为单位的间隔对服务进行主动探测。Cloudflare 每天都有 10w 次免费的 Workers 调用额度,本身的服务在线率也过硬,唯一的缺点可能就是海外节点访问国内服务器的延迟过高了。不过考虑到我们探测的是在线情况而非延迟情况,倒也不是不能接受。

由于服务特性的关系,我们容许一定的访问失败概率。比如五次访问中如果有两三次失败,我们也认为线路是通的,可能只是教务系统的土豆快熟了。因此并不是每一次失败的探测都需要进行告警。另外,我们还需要记录当前的服务状态,一旦服务被认定为下线状态,后续探测失败我们就不再进行告警,直到我们重新判定服务状态为上线(即每段连续的下线时间都只触发一次告警)。Cloudflare Worker 是一种 serverless 服务,且每次执行探测任务的可能都不是同一个 Cloudflare 的边缘节点,因此我们没法使用变量在内存中记录目前的服务状态,需要引入外部数据库来完成短期内的数据存储。

在数据库上,我们必须选择 Cloudflare 自家的在线数据库服务,通过 Cloudflare 自己内部的网络传输数据库查询结果才能得到尽可能低的延迟。在一番考量过后,KV 数据库和 SQL 数据库中,我果断选择了 Cloudflare D1 这个 SQL 数据库(本质是 SQLite),D1 数据库以更长的查询时间换取数据的实时性。Cloudflare 为免费用户提供了每天 500w 行读取和 10w 行写入的免费额度,只要好好加以利用,就不太可能超出限额。后续我还考虑通过这些数据库的数据使用 Cloudflare Worker 构建 uptime status 的后端 API,实现一个类似 status.openai.com 的在线服务状态可视化界面。

OpenAI 的 uptime statusOpenAI 的 uptime status

登陆 wrangler#

wrangler login

项目初始化#

由于之前有过一些编写 Cloudflare Workers 的经验,我深知在 Cloudflare 网页的 code server 编辑器上编辑单文件的 worker.js 的不便,选择使用 Cloudflare 官方推出的工具 wrangler 进行项目的初始化。

npm create cloudflare@latest wjh-monitor

Q: What would you like to start with? A: Hello World example

Q: Which template would you like to use? A: Hello World Worker

Q: Which language do you want to use? A: TypeScript

得到的项目结构得到的项目结构

我们只需要在 index.ts 中编写我们的主要逻辑即可。

数据库初始化#

wrangler d1 create wjh-monitor-db

随后会输出这些内容,我们需要把这些配置文件写入项目的 wrangler.toml 文件中

[[d1_databases]]
binding = "DB"
database_name = "wjh-monitor-db"
database_id = "ffffffff-ffff-ffff-ffff-ffffffffffff"

编写一个 sql 文件创建数据表,并通过 wrangler 创建它

// schema.sql
CREATE TABLE IF NOT EXISTS DATA (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    check_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    check_item VARCHAR(10),
    response_time INTEGER NOT NULL,
    success BOOLEAN NOT NULL,
    online_status BOOLEAN NOT NULL,
    notify BOOLEAN DEFAULT FALSE,
    other TEXT,
    INDEX check_time_idx (check_time)
);
INSERT INTO DATA (
        response_time,
        success,
        online_status,
        notify,
        other
    )
VALUES (100, 1, 1, 0, 'initial');

使用 sql 文件创建远程数据表

wrangler d1 execute wjh-monitor-db --remote --file=./schema.sql

随后我们可以使用 wrangler 在远程数据库上执行 query 命令,并获得相应的结果

wrangler d1 execute wjh-monitor-db --remote --command='SELECT * FROM DATA'

主体逻辑编写#

说实话,代码部分没什么太多好说的,正要了解思路直接去看源码就好。我在这里简单提两点。

  1. 目录下的 worker-configuration.d.ts 定义了 env 变量的类型定义,如果我们在项目中绑定了一些变量(比如我们在 wrangler.toml 中绑定了 d1 数据库,变量名为 DB),需要在这里声明以防止后续 TypeScript 的报错。
  2. export default {} 中是将会被导出的主要函数,Hello World 项目编写了 fetch 函数,这是在 workers 被通过 http 方式访问时所调用的,如果要使用 cron job 功能,我们需要编写 scheduled 函数来被 workers 调用,并在 wrangler.toml 中配置好 crontab 触发器。
  3. 我们在 wrangler.toml 中绑定了 DB 变量作为数据库的快捷访问方式,因此我们可以在代码中通过 D1 数据库的 Workers Binding API 来实现针对数据库的快捷操作,如
    const res = await env.DB.prepare("SELECT * FROM DATA ORDER BY check_time DESC LIMIT 4").run();
    

需要注意的是,workers 不存在上下文,每一次访问的处理都是前后独立的,如果你需要临时存储一些数据,不要使用变量,一定要存入持久化存储的数据库。

预览:

wrangler dev

部署:

wrangler deploy

小插曲#

由于我对后端经验的缺乏,我在编写 sql 语句时没有意识到建立索引的重要性。我在查询时使用了 ORDER BY <未建立索引字段> LIMIT 5 的方式来查询最近五次的记录,这导致数据库不得不在我每次查询时都完整遍历一遍整张表。随着 cronjob 每分钟运行时插入一条新数据,记录的行数随时间增加,每次查询的成本也逐渐增加,最终造成了单日访问八百多万行的记录,超出了 Cloudflare 的免费额度,一度造成了项目被迫下线的风险。

所幸 Cloudflare 没有给我停机,而我也及时定位到了问题并补建了索引,使每日的读取量回到了正常的状态。

蓝色线条为读取,黄色线条为写入蓝色线条为读取,黄色线条为写入

参见#

Cloudflare D1 · Cloudflare D1 docs

Cron Triggers · Cloudflare Workers docs

Cloudflare D1 使用记录