Case Study
Chrome 插件 Node 服务
连接浏览器插件与业务 API 的中间层:无感登录、实时推送与 Go 重构带来的 85% 性能提升。
-85%
接口耗时(800→120ms)
-60%
高频查询响应
-83%
发布耗时(30→5min)
背景
内部运营团队每天要在多个后台之间复制粘贴数据,团队里一名工程师每天大约浪费 30 分钟在重复操作上。我们做了一个 Chrome 插件承接这些操作,但插件不能直接持有业务系统的凭证:于是需要一层中间服务,负责无感登录、权限校验、数据聚合与实时指令下发。
我的角色
- 基于 Midway.js + TypeScript 开发中间层服务,作为插件与后端 API 之间的唯一通道。
- 设计无感登录与凭证托管方案,覆盖 10+ 名团队成员。
- 用 WebSocket 支撑实时数据推送与指令下发。
- 针对 JSON 解析瓶颈,用 Go 重写核心模块。
关键设计
1. 无感登录:凭证留在服务端
插件只持有一个短期会话 token,业务系统的凭证由服务端加密保存并在请求时注入。会话过期时服务端自动续期,插件无需感知:
@Provide()
export class SessionService {
async resolve(userId: string): Promise<Credentials> {
const cached = await this.redis.get(`session:${userId}`);
if (cached) return JSON.parse(cached);
const credential = await this.vault.read(userId);
const refreshed = await this.refresh(credential); // 过期则续期
await this.redis.set(`session:${userId}`, JSON.stringify(refreshed), 'EX', 1800);
return refreshed;
}
}
2. 高频查询加缓存,且能失效
商品、订单模板这类查询结果变化不频繁,却占了大部分请求量。加入 Redis 缓存并对写操作打失效标记后,响应时间下降约 60%。关键在于按业务实体维度失效,而不是粗暴的全量清空。
3. WebSocket 做指令通道
轮询在「等待后台任务完成」的场景下体验很差。改为 WebSocket 后,服务端在任务完成时主动推送结果,插件即时刷新,同时减少了大量空轮询请求。
4. Go 重构解析模块
真正的瓶颈出现在一段复杂的 JSON 解析与字段映射逻辑:单次请求 800ms,其中 70% 消耗在深度遍历与正则匹配上。用 Go 重写为独立服务后:
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 平均响应时间 | 800ms | 120ms |
| P99 响应时间 | 2.4s | 380ms |
| 单机 CPU 占用 | 高(单核打满) | 中等且平稳 |
保留 Node 作为业务编排层,把 CPU 密集型解析下沉到 Go,是这次最划算的架构选择。
结果
- 插件覆盖 10+ 名成员,人均每日节省约 30 分钟重复操作。
- 解析接口响应从 800ms 降至 120ms,降幅约 85%。
- 结合 PM2 与 GitLab CI/CD,发布耗时从 30 分钟缩短至 5 分钟。
复盘
Node 适合 I/O 密集的业务编排,不适合长时间占用 CPU 的计算。把两者分开之后,服务既保持了迭代速度,也拿到了数量级的性能收益。判断标准很简单:看火焰图里时间花在等 I/O,还是在算。