前言:在上一篇中,我们实现了多种网络发送策略,让 SDK 能够把数据发出去。但真实的网络环境是不可靠的——弱网、断网、高频触发都是常态。今天,我们要给 SDK 穿上一层"防弹衣",打造一个永不丢单的健壮系统。
1. 痛点:脆弱的直接发送
如果我们只是简单地调用 fetch,会遇到两个致命问题:
- 流量风暴:用户快速滑动列表,瞬间产生几十个请求,服务器压力山大。
- 数据丢失:用户在地下停车场点击"支付",因无网络导致请求失败,关键转化数据丢失。
为了解决这些问题,我们需要引入两个核心机制:消息队列 (Queue) 和 离线存储 (Store) 。
2. 消息队列:削峰填谷
我们不能来一个发一个,而是要"攒一攒"再发。这叫批处理 (Batching) 。
2.1 智能调度器 (Scheduler)
我们创建一个 Scheduler 类来管理队列。它支持两种优先级:
- immediate (高) :支付、关键点击 -> 立即发送。
- batch (低) :曝光、滚动 -> 攒够 10 条或 3 秒后发送。
// src/core/Scheduler.ts
export class Scheduler {
private queue: TrackerEvent[] = [];
private timer: number | null = null;
addEvent(event: TrackerEvent, priority: 'immediate' | 'batch' = 'batch') {
this.queue.push(event);
// 1. 高优先级:立即发送 (队列里所有数据)
if (priority === 'immediate') {
this.flush();
return;
}
// 2. 低优先级:启动定时器 (3秒兜底)
if (!this.timer) {
this.timer = window.setTimeout(() => this.flush(), 3000);
}
// 3. 队列满了:立即发送 (10条阈值)
if (this.queue.length >= 10) {
this.flush();
}
}
}
3. 离线存储:IndexedDB 兜底
当 flush() 发送失败时,数据不能丢,必须存起来。
3.1 为什么选 IndexedDB?
| 方案 | 容量 | 性能 | 结构化 | 结论 |
|---|---|---|---|---|
| localStorage | 5MB | 同步(卡顿) | 仅字符串 | ❌ 仅适合玩具 |
| IndexedDB | >250MB | 异步 | 支持对象 | ✅ 生产级首选 |
3.2 仓库实现 (Store)
我们封装一个轻量级的 Store 类,屏蔽 IndexedDB 的复杂 API。
// src/core/Store.ts
export class Store {
// 发送失败时调用:存入数据库
async save(events: TrackerEvent[]) {
const db = await this.getDB();
const tx = db.transaction('events', 'readwrite');
events.forEach(e => tx.store.add(e));
}
// 网络恢复时调用:取出所有数据
async getAll() {
// ...读取逻辑
}
}
4. 闭环:自动重试机制
最精彩的部分来了。我们在 Scheduler 中监听浏览器的 online 事件,实现全自动恢复。
// src/core/Scheduler.ts
constructor() {
// 监听网络恢复
window.addEventListener('online', () => {
console.log('🌐 网络已恢复,正在重发积压数据...');
this.retryFailedEvents();
});
}
async retryFailedEvents() {
// 1. 从 Store 取出所有积压数据
const events = await this.store.getAll();
if (events.length === 0) return;
try {
// 2. 尝试重发
await this.sender.send(events);
// 3. 发送成功,清空 Store
await this.store.clear();
console.log('✅ 重发成功!');
} catch (e) {
// 4. 依然失败?没事,数据还在 Store 里,下次再试
console.error('❌ 重发失败,等待下一次机会');
}
}
5. 总结
通过本篇的改造,我们的 SDK 进化了:
- 性能:请求数减少 90%(批处理)。
- 可靠:弱网/断网环境下数据不丢失(离线存储)。
现在,我们的 SDK 已经具备了商业级产品的核心素质。
下一篇,我们将探讨如何让 SDK 走出浏览器,适配 Node.js、小程序 等多种运行时环境,实现真正的跨平台架构。
本文代码已开源,欢迎 Star ⭐ ️