1. 埋点异常场景全景

埋点是客户端数据采集与业务分析的核心基础设施。但在真实的上报链路中,从埋点产生到服务端落地,每个环节都可能引入异常。本章从客户端埋点上报链路出发,逐一剖析三类典型异常——网络异常、数据格式错、服务端拒绝,并定义异常处理的五大目标,为后续基于 Retrofit 的实战设计建立全景认知。

1.1 客户端埋点上报链路

一条埋点数据从用户行为触发到服务端确认接收,在客户端内部经历五个核心阶段。任一阶段失效都会导致数据丢失或延迟。

  • 应用层 — 埋点采集:拦截用户事件、页面曝光、业务行为,生成原始事件对象。
  • 存储层 — 本地缓存:将事件写入 SQLite / MMKV,防止应用被杀死导致数据丢失。
  • 数据层 — 序列化封装:将事件对象转为 JSON / Protobuf,添加公共参数与签名。
  • 网络层 — 网络传输:通过 Retrofit + OkHttp 发起 HTTP POST 请求,携带埋点 body 发送至服务端。
  • 服务层 — 服务端接收:服务端反序列化、校验、持久化,返回成功/失败响应。
客户端埋点上报链路与异常全景 埋点采集 应用层 本地缓存 存储层 序列化封装 数据层 网络传输 网络层 服务端接收 服务层 ❌ 数据格式错 JSON解析失败 · 字段缺失 ❌ 网络异常 超时 · 断网 · 无响应 ❌ 服务端拒绝 HTTP 4xx/5xx · 限流 异常处理目标:零丢失 · 有序性 · 高性能 · 可观测 · 自恢复

1.2 三大异常类型深度解析

根据埋点在链路中实际失效的位置,异常可归为三类。下表从根因、表现和影响范围三个维度进行对比。

异常类型 发生阶段 根因 典型表现 影响范围
网络异常 网络传输层 连接超时、DNS 解析失败、无网、网关超时 OkHttp 回调 onFailure;请求未到达服务端 批量埋点丢失(窗口内所有事件)
数据格式错 序列化 / 反序列化层 字段类型不匹配、必要字段缺失、JSON 语法错误 JSONException;服务端 400 响应 单条埋点失效
服务端拒绝 服务接收层 鉴权失败、频率限流、业务校验不通过 HTTP 401/403/429/503;响应 body 带错误码 批量或单条拒绝,视服务端策略而定
🔍 观察点:网络异常通常由客户端网络环境触发;数据格式错多由版本兼容或字段变更引起;服务端拒绝则与业务策略强相关。三类异常的处理策略在后续章节中截然不同。

1.3 异常处理五大目标

在 Retrofit 框架下设计埋点异常处理时,必须同时达成以下五个目标,缺一不可。

  1. 零丢失:通过本地缓存 + 指数退避重试,确保数据最终送达。
  2. 有序性:保证同 session 内埋点的时间线不被乱序重排。
  3. 高性能:异步上报、批量合并,不阻塞 UI 线程,不导致应用卡顿。
  4. 可观测:异常次数、失败率、重试次数等指标可监控、可告警。
  5. 自恢复:网络恢复后自动补发缓存的埋点,无需用户手动触发。
⚠️ 常见误区:只关注网络重试而忽略数据格式校验,会导致服务端大量 400 响应,浪费带宽与计算资源。异常处理必须从链路全局出发,分层防御。

1.4 埋点数据模型与 Retrofit 接口定义

为了让后续章节的代码实战有统一的数据基础,这里先给出埋点事件的 POJO 模型和对应的 Retrofit 接口声明。后续所有异常处理拦截器、转换器、重试策略都将围绕此接口展开。

// 埋点事件数据模型
public class EventBean {
    private String eventId;                // 事件唯一 ID
    private String eventName;              // 事件名称,如 "page_view"
    private long timestamp;                // 事件发生时间戳 (ms)
    private Map<String, Object> params;    // 业务自定义参数
    private int priority;                  // 上报优先级 0=实时 1=批量

    // 构造函数、getter/setter 省略
}
// 埋点上报 Retrofit 接口
public interface TrackingApi {

    @POST("v1/track")
    @Headers("Content-Type: application/json")
    Call<TrackResponse> uploadEvents(@Body List<EventBean> events);
}
// 服务端通用响应结构
public class TrackResponse {
    private int code;          // 0=成功 非0=业务错误
    private String message;    // 错误描述
    private long serverTime;   // 服务端时间,用于时钟校准
}
📌 设计要点:EventBean 的 priority 字段用于区分实时与批量通道;TrackResponse 的 code 字段用于识别业务层拒绝。这些字段将在后续的异常分类与重试策略中发挥关键作用。