编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 性能优化实践

  • 程序编程原理

  • 稳定性与可靠性

  • 工程化与运维

  • 方案设计思想

    • README
    • 架构与组件

    • 数据与存储

    • 通信与协议

    • 稳定性与安全

    • 端侧专项性

    • 研发的效能

      • 通用轮训方案设计
        • 01.百万设备秒级轮询
          • 1.1 一次定位上报雪崩
          • 1.2 雪崩扩散链路
          • 1.3 反思轮询设计
        • 02.要解决的核心矛盾
          • 2.1 实时与省电
          • 2.2 频率与压力
          • 2.3 主动与被动
          • 2.4 轮询的本质
        • 03.业界主流方案
          • 03.1 主流通信模式
          • 03.2 横向对比矩阵
          • 03.3 选型速查表
        • 04.设计核心原则
          • 04.1 退避抖动原则
          • 04.2 自适应频率
          • 04.3 前后台分级
          • 04.4 失败容错原则
        • 05.方案落地实战
          • 05.1 整体架构
          • 05.2 客户端实现
          • 05.3 长轮询实现
          • 05.4 心跳保活机制
          • 05.5 服务端配合
        • 06.关键问题解决
          • 06.1 雪崩防御
          • 06.2 流量与电量
          • 06.3 后台限制问题
        • 07.常见陷阱与反例
          • 07.1 固定频率反例
          • 07.2 不退避反例
          • 07.3 全设备同步反例
        • 08.演进路线
          • 08.1 V1 简单短轮询
          • 08.2 V2 长轮询 + 退避
          • 08.3 V3 推送 + 兜底轮询
        • 09.总结与决策
          • 09.1 上线检查表
          • 09.2 选型决策树
      • 状态机设计的思想
      • 通用搜索方案设计
      • 通用权限模型方案
  • 专栏
  • 方案设计思想
  • 研发的效能
杨充
2025-02-20
目录

通用轮训方案设计

# 27.通用轮询方案设计

本篇定位:轮询是分布式/客户端开发里"最简单也最容易写错"的机制——做不好就是"耗电、耗流量、雪崩、消息延迟"四件套。本文从一次"百万设备每秒轮询打挂网关"的故事讲起,回答三个核心问题——轮询的本质矛盾在哪?短轮询/长轮询/推送怎么选?怎么设计一套既稳又省的通用轮询框架?

# 目录介绍

  • 01.百万设备秒级轮询
    • 1.1 一次定位上报雪崩
    • 1.2 雪崩扩散链路
    • 1.3 反思轮询设计
  • 02.要解决的核心矛盾
    • 2.1 实时与省电
    • 2.2 频率与压力
    • 2.3 主动与被动
    • 2.4 轮询的本质
  • 03.业界主流方案
    • 03.1 主流通信模式
    • 03.2 横向对比矩阵
    • 03.3 选型速查表
  • 04.设计核心原则
    • 04.1 退避抖动原则
    • 04.2 自适应频率
    • 04.3 前后台分级
    • 04.4 失败容错原则
  • 05.方案落地实战
    • 05.1 整体架构
    • 05.2 客户端实现
    • 05.3 长轮询实现
    • 05.4 心跳保活机制
    • 05.5 服务端配合
  • 06.关键问题解决
    • 06.1 雪崩防御
    • 06.2 流量与电量
    • 06.3 后台限制问题
  • 07.常见陷阱与反例
    • 07.1 固定频率反例
    • 07.2 不退避反例
    • 07.3 全设备同步反例
  • 08.演进路线
    • 08.1 V1 简单短轮询
    • 08.2 V2 长轮询 + 退避
    • 08.3 V3 推送 + 兜底轮询
  • 09.总结与决策
    • 09.1 上线检查表
    • 09.2 选型决策树

# 01.百万设备秒级轮询

# 1.1 一次定位上报雪崩

某车联网厂商:100 万台车辆每秒上报一次定位——某天网关扩容时短暂 502,100 万设备同时进入"重试地狱":

  • 设备端写死:每秒上报,失败 1 秒后立即重试
  • 网关短暂故障 5 秒后恢复
  • 但所有失败的请求堆积成"双倍流量"压上来——网关瞬间被压垮
  • 进一步级联到数据库、消息队列——整套系统瘫痪 30 分钟
gantt
    title 雪崩故障时间线
    dateFormat HH:mm:ss
    axisFormat %H:%M:%S
    
    section 正常轮询
    100w QPS 上报      :a, 09:00:00, 5m
    
    section 故障窗口
    网关 502         :crit, b, 09:05:00, 5s
    所有设备失败堆积   :crit, c, 09:05:00, 5s
    
    section 灾难放大
    网关恢复 双倍重试 :crit, d, 09:05:05, 30m
    级联打挂 DB / MQ  :crit, e, 09:05:10, 30m
    全网瘫痪          :crit, f, 09:05:10, 30m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 1.2 雪崩扩散链路

flowchart TD
    A[100w 设备每秒轮询] --> B[网关短暂 502 5 秒]
    B --> C[100w 请求失败]
    C --> D[设备端 1 秒后重试]
    D --> E[新一秒的请求 + 重试请求]
    E --> F[200w QPS 涌入]
    F --> G[网关被压垮]
    G --> H[级联 DB/MQ 全部打挂]
    
    Cause[根因] --> R1[固定频率 1s 高频]
    Cause --> R2[失败立即重试 无退避]
    Cause --> R3[所有设备齐步走 无抖动]
    Cause --> R4[网关无限流保护]
    
    style G fill:#ffebee
    style H fill:#ffebee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 1.3 反思轮询设计

事后这个团队总结了三个最深刻的教训:

  1. 轮询频率必须按需——不是越快越好,1 秒和 30 秒可能业务效果一样
  2. 失败必须指数退避——立即重试是雪崩催化剂
  3. 必须随机抖动——避免百万设备齐步走

轮询的难点不在"实现"——而在"如何让 100 万个客户端不打架"。

# 02.要解决的核心矛盾

# 2.1 实时与省电

频率 实时性 电量影响 流量
1 秒 极好 极差 大
10 秒 好 一般 中
1 分钟 一般 好 小
10 分钟 差 极好 极小
1 小时 极差 不影响 忽略

核心思考:业务真的需要 1 秒级实时吗?很多场景"5 秒和 30 秒"用户感知不到差别。

# 2.2 频率与压力

graph LR
    A[N 个客户端] --> B[频率 f]
    B --> C[服务端 QPS = N × 1/f]
    
    Note1[100w 设备 × 每秒 1 次 = 100w QPS]
    Note2[100w 设备 × 每分钟 1 次 = 1.7w QPS]
    Note3[100 倍差距]
    
    style Note3 fill:#fff3e0
1
2
3
4
5
6
7
8
9

# 2.3 主动与被动

mindmap
  root((通信模式))
    主动 - 客户端拉
      短轮询
      长轮询
      实现简单
      流量大
    被动 - 服务端推
      WebSocket
      Server-Sent Events
      MQTT 推送
      实时性好
      实现复杂
    混合
      推送为主
      轮询兜底
      失败时降级
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2.4 轮询的本质

轮询 = 客户端用"频率"换取"实时性"

它的代价 = N 个客户端 × 1/频率 = 服务端压力。

设计的核心 = 在"实时"和"压力"之间找到业务可接受的平衡点。

# 03.业界主流方案

# 03.1 主流通信模式

模式 描述 实时性 复杂度
短轮询 定时发请求 看频率 ⭐
长轮询 服务端 hold 请求 准实时 ⭐⭐
WebSocket 双向长连接 实时 ⭐⭐⭐
SSE 服务端单向推送 实时 ⭐⭐
MQTT 物联网长连接 实时 ⭐⭐⭐
Push 通知 系统级推送 准实时 ⭐⭐

# 03.2 横向对比矩阵

维度 短轮询 长轮询 WebSocket 推送
实时性 ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
服务端压力 高 中 低 极低
流量消耗 高 中 低 极低
电量影响 大 中 中 小
实现复杂度 低 中 高 中
穿透 NAT ✅ ✅ ⚠️ ✅
典型场景 状态查询 通知/聊天 直播弹幕 离线消息

# 03.3 选型速查表

flowchart TD
    Q1{需要实时?} 
    
    Q1 -->|秒级延迟可接受| Short[短轮询<br/>简单 + 兜底]
    Q1 -->|准实时| Q2{消息频率?}
    Q1 -->|强实时<br/>< 100ms| WS[WebSocket]
    
    Q2 -->|低频| Long[长轮询]
    Q2 -->|中高频| WS2[WebSocket]
    
    Q3{App 离线?} -->|需要送达| Push[Push 通知]
    
    style Short fill:#e3f2fd
    style Long fill:#e8f5e8
    style WS fill:#fff3e0
    style Push fill:#f3e5f5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 04.设计核心原则

# 04.1 退避抖动原则

铁律:任何失败重试都要指数退避 + 随机抖动。

class BackoffStrategy(
    private val baseMs: Long = 1000,
    private val maxMs: Long = 5 * 60_000,
    private val multiplier: Double = 2.0
) {
    private var attempt = 0
    
    fun nextDelay(): Long {
        attempt++
        // 指数退避 1s → 2s → 4s → ... 上限 5 分钟
        val expDelay = (baseMs * multiplier.pow(attempt - 1).toLong())
            .coerceAtMost(maxMs)
        // ±50% 随机抖动 防齐步走
        val jitter = (expDelay * 0.5 * (Math.random() - 0.5)).toLong()
        return expDelay + jitter
    }
    
    fun reset() { attempt = 0 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

抖动的威力:

graph LR
    A[100w 设备同时失败] --> B{有抖动?}
    B -->|无| C[100w QPS 同时打过来]
    B -->|有 ±50%| D[QPS 散布在<br/>0.5x - 1.5x 时间窗]
    
    style C fill:#ffebee
    style D fill:#e8f5e8
1
2
3
4
5
6
7

# 04.2 自适应频率

根据"是否有变化"调整轮询频率:

stateDiagram-v2
    [*] --> Active: 启动
    Active: 高频 5s
    Idle: 低频 1min
    Sleep: 极低频 5min
    
    Active --> Idle: 连续 N 次无变化
    Idle --> Active: 检测到变化
    Idle --> Sleep: 长时间无变化
    Sleep --> Active: 用户操作触发
1
2
3
4
5
6
7
8
9
10

典型实现:

class AdaptivePoller {
    private var interval = 5_000L  // 起步 5 秒
    private var unchangedCount = 0
    
    fun onResult(changed: Boolean) {
        if (changed) {
            interval = 5_000L      // 有变化 → 高频
            unchangedCount = 0
        } else {
            unchangedCount++
            interval = when {
                unchangedCount > 20 -> 5 * 60_000L  // 5 分钟
                unchangedCount > 5  -> 60_000L      // 1 分钟
                else -> interval
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 04.3 前后台分级

App 状态决定轮询策略:

App 状态 推荐策略
前台 + 用户活跃 高频(5-10 秒)
前台 + 空闲 中频(30 秒-1 分钟)
后台运行 低频(5 分钟+)
完全退出 不轮询 / 用 Push
充电 + Wi-Fi 可适度提高
低电量 主动降频

# 04.4 失败容错原则

flowchart LR
    Req[发起轮询请求] --> Result{结果}
    
    Result -->|成功| OK[重置退避计数]
    Result -->|网络错误| Retry[退避重试]
    Result -->|限流 429| Backoff[长退避]
    Result -->|服务错误 5xx| Retry2[退避重试]
    Result -->|业务错误 4xx| Stop[停止 + 上报]
    
    style OK fill:#e8f5e8
    style Stop fill:#ffebee
1
2
3
4
5
6
7
8
9
10
11

# 05.方案落地实战

# 05.1 整体架构

graph TB
    subgraph "客户端"
        Lifecycle[生命周期监听]
        Strategy[策略引擎<br/>前台/后台/省电]
        Poller[轮询调度器]
        Backoff[退避器]
        Net[网络请求]
    end
    
    subgraph "服务端"
        Gate[网关 + 限流]
        Service[业务服务]
        Cache[(缓存)]
    end
    
    Lifecycle --> Strategy
    Strategy --> Poller
    Poller --> Backoff
    Backoff --> Net
    Net --> Gate
    Gate --> Service
    Service --> Cache
    
    Service -->|响应 + 控制频率| Net
    Net --> Poller
    
    style Strategy fill:#fff3e0
    style Backoff fill:#e8f5e8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 05.2 客户端实现

通用轮询框架:

class GeneralPoller<T>(
    private val task: suspend () -> Result<T>,
    private val onChange: (T) -> Unit,
    private val config: PollerConfig
) {
    private var job: Job? = null
    private val backoff = BackoffStrategy()
    private val adaptive = AdaptivePoller(config.minIntervalMs, config.maxIntervalMs)
    private var lastValue: T? = null
    
    fun start(scope: CoroutineScope) {
        stop()
        job = scope.launch {
            while (isActive) {
                runCatching { task() }
                    .onSuccess { result ->
                        backoff.reset()
                        if (result is Result.Success && result.data != lastValue) {
                            onChange(result.data)
                            adaptive.onResult(changed = true)
                            lastValue = result.data
                        } else {
                            adaptive.onResult(changed = false)
                        }
                        delay(adaptive.currentInterval())
                    }
                    .onFailure {
                        delay(backoff.nextDelay())  // 失败退避
                    }
            }
        }
    }
    
    fun stop() {
        job?.cancel()
        backoff.reset()
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 05.3 长轮询实现

长轮询 = 服务端 hold 住请求,有数据才返回。

sequenceDiagram
    participant C as Client
    participant S as Server
    
    C->>S: GET /poll?lastId=100
    Note over S: hold 30s 等数据
    
    alt 30s 内有新数据
        S-->>C: 返回 newData (lastId=120)
        C->>S: 立即下一次 GET /poll?lastId=120
    else 30s 超时无数据
        S-->>C: 204 No Content
        C->>S: 立即下一次 GET /poll?lastId=100
    end
1
2
3
4
5
6
7
8
9
10
11
12
13
14

服务端实现要点:

@GetMapping("/poll")
suspend fun longPoll(@RequestParam lastId: Long): Response {
    val timeout = 30_000L
    val deadline = System.currentTimeMillis() + timeout
    
    while (System.currentTimeMillis() < deadline) {
        val data = repo.findAfter(lastId)
        if (data.isNotEmpty()) {
            return Response.ok(data)
        }
        delay(500)  // 短暂休眠后再查
    }
    return Response.noContent()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 05.4 心跳保活机制

长连接场景下的心跳——既检测连接是否健康,也保持 NAT 映射。

graph LR
    A[长连接建立] --> B[心跳定时器]
    B --> C[发心跳包 60s]
    C --> D{收到 PONG?}
    
    D -->|是| C
    D -->|3 次未收到| Recon[认为断开<br/>重连]
    
    style Recon fill:#fff3e0
1
2
3
4
5
6
7
8
9

典型心跳间隔经验值:

  • WiFi:30-60 秒
  • 4G/5G:30-60 秒(NAT 超时通常 1-3 分钟)
  • 弱网络/海外:调短到 20 秒
  • 后台:5-10 分钟(节省电量)

# 05.5 服务端配合

服务端控制客户端频率——通过响应字段下发。

{
    "data": [...],
    "_polling": {
        "next_interval_ms": 30000,   // 下次多久后再来
        "max_concurrent": 1000,       // 服务端建议并发上限
        "backoff_on_503": true        // 服务端过载时建议退避
    }
}
1
2
3
4
5
6
7
8

好处:服务端能动态调控全局压力——客户端无需改版本。

# 06.关键问题解决

# 06.1 雪崩防御

开篇 100 万设备雪崩的解决:多层防御。

flowchart TD
    A[问题] --> B[100w 设备齐步走 + 失败堆积]
    
    Solution[多层防御]
    Solution --> S1[1 客户端: 指数退避]
    Solution --> S2[2 客户端: 随机抖动 ±50%]
    Solution --> S3[3 客户端: 自适应频率]
    Solution --> S4[4 网关: 令牌桶限流]
    Solution --> S5[5 服务端: 动态下发频率]
    Solution --> S6[6 服务端: 熔断保护下游]
    
    style Solution fill:#e8f5e8
1
2
3
4
5
6
7
8
9
10
11
12

# 06.2 流量与电量

手机端 1 天的轮询代价:

频率 一天请求数 流量(按 1KB/请求) 电量影响
1 秒 86,400 ~85MB 严重
10 秒 8,640 ~8.5MB 中等
1 分钟 1,440 ~1.4MB 轻微
5 分钟 288 ~280KB 几乎无

实战经验:1 分钟以下频率必须做"前后台分级",否则上线后必被用户骂耗电。

# 06.3 后台限制问题

iOS / Android 都对后台轮询有严格限制:

平台 限制
iOS 后台几乎不能执行普通代码,只能用 Push / Background Fetch
Android 6+ Doze 后台休眠期间禁止网络访问
Android 8+ 后台 Service 受限
小米/华为/OPPO 厂商定制 ROM 杀后台更狠

应对:

  • 后台尽量靠 Push 通知 唤醒
  • 实在需要:WorkManager + 长间隔(最低 15 分钟)
  • 关键消息:APNs/FCM/厂商推送 推送

# 07.常见陷阱与反例

# 07.1 固定频率反例

反例:固定写死"每 1 秒轮询一次"——忽略业务实际变化频率。

教训:

  • 用户大部分时间没有变化 → 1 秒一次是浪费
  • 用自适应频率:有变化加速、无变化减速

# 07.2 不退避反例

反例:失败立即重试——5 秒故障 → 双倍流量 → 雪崩。

教训:

  • 失败必须退避(指数退避)
  • 退避必须有上限(避免永远不重试)
  • 必须有抖动

# 07.3 全设备同步反例

反例:所有设备启动时都"立即上报一次"——百万设备同时上报 → 网关瞬时压力暴涨。

教训:

  • 启动时随机延时 0-N 秒
  • 设备 ID 哈希分散到时间窗
  • 服务端可下发"建议启动延时"
mindmap
  root((三大反例))
    固定频率
      不感知业务变化
      浪费资源
      用自适应
    不退避
      雪崩催化剂
      流量翻倍
      指数退避 + 抖动
    全设备同步
      启动风暴
      网关被打
      时间分散
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 08.演进路线

# 08.1 V1 简单短轮询

特征:业务起步、设备数少。

做法:

  • 固定频率轮询
  • 无退避
  • 无抖动

适用阶段:< 1w 设备 / POC

# 08.2 V2 长轮询 + 退避

特征:业务规模化、追求实时性。

做法:

  • 长轮询为主
  • 指数退避 + 随机抖动
  • 自适应频率
  • 前后台分级

适用阶段:万级-百万级设备

# 08.3 V3 推送 + 兜底轮询

特征:超大规模、极致体验。

做法:

  • 长连接 / WebSocket / MQTT 主推
  • Push 通知唤醒
  • 短轮询作为兜底
  • 服务端动态下发频率
  • 全链路监控

适用阶段:千万级设备 / 头部 App

flowchart LR
    V1[V1 短轮询<br/>起步] --> V2[V2 长轮询 + 退避<br/>主流]
    V2 --> V3[V3 推送 + 兜底轮询<br/>头部]
    
    style V1 fill:#e3f2fd
    style V2 fill:#e8f5e8
    style V3 fill:#fff3e0
1
2
3
4
5
6
7

# 09.总结与决策

# 09.1 上线检查表

轮询方案上线前对照:

  • [ ] 频率经过业务评估(不是越快越好)
  • [ ] 失败指数退避 + 随机抖动
  • [ ] 退避有上限(避免永不重试)
  • [ ] 自适应频率(按变化情况调整)
  • [ ] 前后台分级
  • [ ] 启动随机延时(防齐步走)
  • [ ] 网关有限流保护
  • [ ] 服务端可动态下发频率
  • [ ] 离线场景用 Push 兜底
  • [ ] 监控(QPS、失败率、电量影响)
  • [ ] 故障演练(网关挂、设备雪崩)
  • [ ] 流量 / 电量基线测试

# 09.2 选型决策树

flowchart TD
    Start([我要做轮询]) --> Q1{实时性要求?}
    
    Q1 -->|分钟级| Short[短轮询<br/>最简单]
    Q1 -->|秒级| Long[长轮询<br/>实时 + 省流量]
    Q1 -->|毫秒级| WS[WebSocket / MQTT<br/>长连接]
    
    Q2([App 离线?]) --> Off{关键消息?}
    Off -->|是| Push[Push 通知<br/>必须用]
    Off -->|否| Skip[不需要后台轮询]
    
    Q3([设备数量?]) --> N{规模?}
    N -->|< 1w| Simple[简单实现就够]
    N -->|> 100w| Defense[必须多层防雪崩]
    
    style Short fill:#e3f2fd
    style Long fill:#e8f5e8
    style WS fill:#fff3e0
    style Push fill:#f3e5f5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

最后一句话:轮询的难点不在写代码——而在 如何让百万客户端"散步而不是齐步走"。开篇 100 万设备雪崩的根因,就是"固定 1 秒 + 不退避 + 不抖动"三件套。

好的轮询方案 = 频率自适应、失败退避抖动、前后台分级、推送兜底。

上次更新: 2026/06/07, 10:26:12
离线包与预加载方案
状态机设计的思想

← 离线包与预加载方案 状态机设计的思想→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式