ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

移动端全链路网络优化实践:从DNS到弱网治理

2026/9/16 10:34:34 拓冰建站 浏览量
移动端全链路网络优化实践:从DNS到弱网治理 每次车从隧道出来导航总要“愣”几秒才能重新定位、重新拉路况很多人把这归结为“信号不好”。但作为搞移动端网络优化的人我心里清楚这背后其实是 DNS 解析失效、连接重建、请求超时重试、数据包排队这一连串问题在排队爆发。用户不管你是哪一层出了问题他们只记得“导航卡了一下”。高德 APP 这样的地图应用网络优化和普通工具类 App 完全不是一个量级。普通 App 的请求大多是用户主动刷出来的地图应用却是用户在高速移动中、在信号忽强忽弱的环境里、在一秒钟内可能发出几十个请求的场景下使用。我在这条链路上踩了不少坑也沉淀了一些切实有效的优化手段这篇文章就把从 DNS 到弱网的全链路优化思路完整梳理一遍。无论你是在做移动端基础组件还是单纯对这个话题感兴趣都可以参考这里的实践经验。1. 导航场景下的网络全貌为什么通用优化手段不够用1.1 移动网络环境比想象中更恶劣很多做后端优化的同学对网络的认知还停留在“机房到机房”的模型网络是相对稳定的延迟主要在跨地域链路上。但移动端完全不是这么回事尤其是地图导航场景。一次典型的地图请求链路是这样的蜂窝基站或者 Wi-Fi 路由器 → 运营商网关 → 公网骨干 → CDN 或者源站服务器然后原路返回。这中间任何一段出现抖动用户体感就会出问题。更麻烦的是移动端用户的位置是持续变化的相当于一直在不同的基站之间切换每一次切换都可能伴随 IP 地址变化、TCP 连接断开、DNS 缓存失效。高德这种应用还有几个邻人头疼的数据特征突发请求量极高手指滑动地图一秒钟内可能触发二三十个瓦片图片请求请求还都是小而碎的类型。网络环境割裂地下车库、隧道、高架桥下、地铁车厢、电梯里这些场景的信号衰减路径完全不同没有一套固定参数能适配所有情况。实时性要求苛刻路径规划晚一秒返回车就可能开错一个路口路况数据晚十秒到达用户看到的可能就是过期信息。我经常举一个例子信号满格和网络好用之间有时候隔着一整条街。移动网络里信号强度RSRP只代表接收功率不代表数据传输速率。你可能在某个商场地下一层看到手机信号满格但实际丢包率高达 30%因为周围全是承重墙带来的多径干扰。用“信号好不好”来判断网络质量是用户层面最直观的理解但工程上必须拆得更细。1.2 用户体感与网络指标的复杂对应关系网络优化做久了会有一个共识技术指标好不好和用户觉得好不好用中间没有绝对的对等关系。用户不会看你的 DNS 解析耗时降低了多少毫秒他们只会在意两件事内容出来得快不快以及有没有出现中断、白屏、转圈。但工程上又必须用指标来驱动优化。我们内部把用户可感知的问题拆成几类找路慢请求发出去迟迟等不到响应对应的是连接失败、超时、重试。图片糊瓦片加载不出来地图区域是灰的对应的是图片请求被阻塞或超时。卡顿交互没有响应对应的是主线程被网络回调阻塞或者数据包排队导致 CPU 大量消耗在序列化和解压上。完全不可用对应的是本地网络完全断开、DNS 解析彻底失败、服务器拒绝服务。所以全链路优化的目标从来不是追求单一指标最优而是把整条链路的“坑”都填平保证用户在最差的环境下也能至少完成核心任务。后面所有优化手段都是围绕这个目标展开的。2. DNS 解析优化把找 IP 的第一跳握在自己手里2.1 传统 DNS 在移动端的三大顽疾DNS 是网络请求的第一步但也是长期被忽视的一步。在移动端传统 DNS 解析暴露出来的问题比桌面端和服务器端严重得多。第一个问题是解析慢。运营商 LocalDNS 有自己的缓存策略命中缓存时可能只要几毫秒但一旦缓存未命中就要递归查询上级 DNS 服务器耗时可能攀升到几百毫秒甚至数秒。最麻烦的是这个耗时完全不可控你无法预判这次请求是快是慢。在地图场景里用户从隧道出来的一瞬间需要立刻恢复定位数据DNS 慢了两秒界面就转两秒的圈。第二个问题是内容被劫持或污染。这种情况在不同网络环境下有过不同程度的体现部分公网 Wi-Fi 会篡改 DNS 响应把域名解析到错误 IP有些网络还会在解析失败时直接给你返回一个广告页面。对地图应用来说域名被劫持的后果不只是打不开页面而是导航数据被替换、位置请求被拦截属于直接可感知的严重故障。第三个问题是调度失灵。传统 DNS 是按“发起请求的出口 IP”来调度流量的。但手机在移动网络下处于运营商 NAT 之后LocalDNS 看到的是出口网关的 IP不是用户真实所在的位置。这就导致明明用户在上海内容调度却把他导到了北京的节点。地图瓦片、路况数据的加载延迟因此凭空多出几十毫秒。2.2 HTTPDNS 方案客户端主动出击解决上述问题的主流方案是 HTTPDNS。核心思路很简单客户端不再走系统默认的 LocalDNS 解析流程而是直接通过 HTTP 接口向专门的 DNS 服务端请求域名对应的 IP。高德这种体量的应用HTTPDNS 基本是标配了。具体接入时的逻辑是这样的App 启动时从配置中心拉取 HTTPDNS 服务商的地址列表。需要解析域名时直接向这个 HTTP 接口发起请求携带 App 标识、用户所在网络类型、经纬度等信息。服务端综合用户位置、运营商、服务可用性返回一组排序好的 IP 地址和各自的 TTL。客户端拿到结果后缓存到内存和本地存储中后续请求直接使用这些 IP。这里有个容易踩坑的点很多团队第一次接入 HTTPDNS 时只是把域名解析换成了 HTTP 请求但缓存策略还是沿用系统 DNS 的逻辑结果收益不大。HTTPDNS 的核心优势不只是“解析快”而是“结果可定制”。你可以根据自己的业务特点调整 TTL 长短、IP 排序权重、甚至指定某个特殊区域的用户走特定节点。这个能力才是它价值的真正体现。2.3 缓存、预取与降级DNS 的容灾设计DNS 优化不能只盯着“解析过程”客户端这侧的缓存和降级策略同样关键。我在实际项目中建立的缓存体系分三层内存缓存TTL 设置为 60 到 120 秒保证短时间内高频请求不重复走 HTTPDNS 接口。本地持久化上次解析成功的记录会写入本地文件App 冷启动时直接读取不等网络请求返回就能发起业务请求。网络切换触发刷新从 Wi-Fi 切到蜂窝网络、或者从 4G 切换到 5G 时主动清除当前缓存并重新解析。因为不同网络路径下最优的接入节点可能完全不同。预取机制的收益也很大。地图上可能有几十个需要访问的域名但冷启动时不可能全部预解析否则浪费流量和电量。我们的做法是根据用户进入的页面和即将发起的请求动态计算“接下来一分钟内最可能用到的域名列表”提前 30 秒左右发起异步解析。最后是降级策略。HTTPDNS 服务商本身也可能出故障必须准备好 Plan B。我们设置的降级路径是主 HTTPDNS → 备用 HTTPDNS → 系统 LocalDNS。这个降级不是简单的“请求失败就换”而是有一个失败置顶机制连续多次解析失败的域名自动切换到备选通道同时上报监控方便后续排查。注意降级到系统 LocalDNS 时要同时关闭本地持久化缓存避免把之前 HTTPDNS 的结果和系统解析结果混用。两个不同来源的 IP 对全局调度秩序的影响是实测中比较容易忽视的坑。2.4 解析结果的 IP 质量排序HTTPDNS 返回的 IP 不是随机的服务端会按照调度策略排序但客户端不能盲信这个顺序。我们的做法是在客户端做“IP 质量打分”。每个 IP 根据历史请求的成功率、连接耗时、首包耗时计算一个质量分优先使用分数最高的 IP。如果某个 IP 连续失败超过阈值自动把它拉入惩罚列表短时间内不再使用。这个机制在弱网环境下特别有用——服务端调度的信息再全也不如客户端在真实网络里踩出来的数据准确。DNS 这层优化做完之后解析耗时整体下降很明显但真正让用户感知到变化的是把解析和后续的连接建立作为一个整体来协同优化这就到了下一层。3. 连接建立优化每一层通信协议都在省一个 RTT3.1 TCP 细节从 Nagle 算法到 KeepAliveDNS 拿到 IP 之后下一步是建立 TCP 连接。一个 TCP 连接从客户端发起 SYN 到最终建立至少要一个 RTT三次握手的前两次。在正常网络下这一个 RTT 也就几十毫秒但在弱网环境下可能膨胀到几秒。这一层的优化核心是“减少不必要的连接新建”和“让现有的连接更高效”。TCP 层面有几个容易被忽略的细节。第一个是TCP_NODELAY选项。默认情况下TCP 会启用 Nagle 算法把小包积攒起来一并发送这在传输大量数据时能提高吞吐但在地图这种需要发小请求、立即拿响应的场景就会增加延迟。我们会确保所有对时延敏感的业务请求都开启TCP_NODELAY让每个小包立即发送不等下一批数据凑齐。第二个是 KeepAlive 参数的调整。系统默认的 TCP KeepAlive 探测间隔是 2 小时这个值对移动端来说太长了。想想这个场景用户开着导航进了电梯网络短暂断开再恢复时App 还在使用之前那条已经死掉的连接请求全都超时。我们把 KeepAlive 探测间隔缩短到 5 分钟级别同时配合客户端主动的空闲探活机制快速识别并回收死连接。3.2 TLS 握手从 2-RTT 到 0-RTT现在的移动端流量基本都是 HTTPSTLS 握手在连接建立后还要额外消耗 1 到 2 个 RTT。这一层加密是有代价的尤其对地图这种高频请求场景。TLS 1.2 时代握手需要一个完整往返拿到服务器证书再加上密钥协商差不多 2 个 RTT。TLS 1.3 把握手压缩到了 1 个 RTT还支持 0-RTT 会话恢复——客户端如果之前和服务器通信过可以在第一个数据包里直接带上应用数据省掉整个握手过程。但 0-RTT 不是免费的午餐。它存在重放攻击风险客户端离线打包发送的数据可能被恶意第三方截获后重放。所以我们的策略是分级使用只有幂等、无副作用的请求比如路况查询才开 0-RTT涉及账户信息的请求老老实实走完整的 TLS 1.3 握手。还有一个容易被忽视的细节是证书链。有些服务端的证书链包含多级中间证书每次握手都需要把整个证书链传给客户端弱网下这个传输耗时可能畸形地放大。我们的做法是精简证书链确保服务端只下发必要的证书层级同时启用 OCSP Stapling把证书有效性查询的结果由服务器直接附带在握手阶段避免客户端再去额外访问 OCSP 服务器。OCSP 服务器在弱网下经常连不上这一步处理不好会拖垮整个握手的耗时。3.3 HTTP/2 与连接池能复用的资源绝不上路重造HTTP/1.1 时代浏览器和 App 的并发连接数都有限制一个域名下建立的连接多了还会被服务端拒绝。地图应用的请求碎片化严重如果每个请求都新建连接再关闭光是握手开销就能让首屏时间翻倍。升级到 HTTP/2 之后连接复用和并发能力都大幅提升多路复用一条长连接上可以同时跑多个请求请求之间各自独立的 Stream 互不阻塞解决了 HTTP/1.1 的队头阻塞问题。头部压缩HPACK地图请求的 Header 里有大量重复字段User-Agent、Accept、Cookie 等HPACK 把这些内容用索引替换流量节省非常可观。连接预算降低原本一个页面要开二三十个连接现在一个域名基本维持一条长连接就够了对弱网场景意义重大。连接池的设计上我们坚持两个原则。第一核心域名连接预建立冷启动时 App 会预判用户最先访问的域名提前建立好连接等用户开始操作时直接发请求。第二空闲连接不过度保留长连接虽然好但移动网络下的连接生命周期比带宽充足的光线网络要短得多。我们会设置一个合理的空闲超时值超过这个时间就主动关闭避免为了省一次握手而占着一个可能已经半死的连接。经验补充连接池的大小不是越大越好。每个连接在客户端和服务端都要占用内存和端口资源连接数太多反而可能触发系统的资源保护机制。在实际调优中单个域名的连接池维持在 2 到 4 条是多数场景的合理区间具体数值根据请求并发模型压测后确定。4. 弱网请求治理超时、重试与压缩的平衡艺术4.1 场景化超时一刀切的超时时间是最差设计弱网环境下的请求治理第一个要解决的问题是“等多久才算失败”。很多团队习惯配置一个全局超时时间比如 10 秒所有请求统一执行。这种一刀切的设计问题很大。地图场景的请求天然分轻重缓急。路径规划是用户当下的核心任务等 10 秒用户可能还在忍受范围里但如果一个瓦片图片请求等了 10 秒才失败用户早就滑到别的地方去了。所以我们的超时设计是分级的交互级请求瓦片加载、路况刷新、关键词联想超时时间 3 到 5 秒快速失败让用户重新操作宁可慢一点也不要一直转圈。核心任务请求路径规划、路线详情、导航起终点确认超时时间放宽到 8 到 10 秒给业务足够的时间在弱网下努力一把。后台静默请求预加载、缓存刷新超时时间最长但配合低优先级调度不影响用户正在执行的操作。这套策略运行一段时间后我们又加了一个动态调节维度根据当前网络的实测质量自动调整超时。网络质量好的时候把超时缩短避免失败请求拖太久网络质量差的时候反而要适当延长超时因为这个时候重试的成本比重试等待更高。动态调节的依据来自客户端持续采集的网络质量分而不是简单的信号强度。4.2 重试策略幂等、退避与网络兜底超时之后一般伴随重试但重试设计不好往往会放大问题。我见过最典型的反面案例线上某个接口偶发超时客户端统一在 1 秒后重试结果瞬时流量翻倍把服务端直接打崩原本偶发的超时变成了系统性超时。安全的重试策略要满足几个条件第一请求必须幂等。GET、HEAD、PUT 这类请求天然幂等重试没有副作用。POST 请求要加幂等键服务端根据这个键识别是否为同一个业务请求保证重复发送不会导致重复下单、重复上报这类问题。第二重试要有退避和抖动。固定间隔重试会导致客户端之间同步共振。我们的默认策略是 1 秒、2 秒、4 秒的指数退避并在每次重试之间加上随机抖动。最大重试次数控制在 3 次以内再多的重试只是浪费电量和带宽。第三换网络重试是最高性价比的手段。弱网环境下很多请求失败是因为当前网络本身不可用重试再多次也不会成功。正确的做法是监听网络切换事件从 Wi-Fi 切到蜂窝网络、或者从 4G 切换到 5G 时把这些在途的失败请求主动重发一次。实测下来这个策略的成功率提升非常可观远高于同网络下的多次重试。4.3 数据瘦身从 JSON 到 Protobuf 到增量更新网络请求的响应体大小直接决定弱网下的传输时间。这一点上我们做了三层收敛。第一层是格式升级。客户端和服务端的通信协议从 JSON 逐步迁到了 Protobuf。同样一个路径规划结果JSON 序列化后可能 200KBProtobuf 大概 40KB体积相差 5 倍左右。再加上压缩算法从 gzip 切到 Brotli在支持的环境下响应体的传输时间能压缩接近八成。这里要注意Protobuf 在移动端的解析性能也远好于 JSON对 CPU 资源和内存占用都是一次正优化。第二层是数据裁剪。地图场景有很多点到点的数据服务端会按需返回。比如路况详情正常网络下返回精细到路段的实时速度弱网下则返回粗粒度的畅通/缓行/拥堵三色信息。数据量小了用户反而更容易看懂当前路况。信息降级不是功能倒退而是根据网络条件动态适配信息服务形态。第三层是增量更新。地图基础数据有很强的静态属性没必要每次全量拉取。我们建立了增量更新机制客户端记录本地数据版本号请求时带上版本号服务端只返回变更的部分。地图瓦片也类似相同层级的瓦片做过哈希指纹对比内容没变就直接复用本地缓存不再传输重复数据。4.4 弱网测试怎么做才算靠谱优化做得好不好不能靠玄学要靠可控的弱网测试环境。常用的工具是 iOS 的 Network Link Conditioner、Android 下的 adb 网络限速、PC 端的 Charles 和 Fiddler。Fiddler 的弱网模拟功能很适合前后端联调阶段快速验证但它的模拟粒度偏粗一般只能设置固定的延迟和带宽值。真正靠谱的弱网测试要覆盖三类混合场景而不是简单地把延迟调大高延迟模拟 RTT 200ms 到 1000ms 的链路测试超时逻辑和用户等待体验。高丢包模拟 5% 到 30% 的丢包率测试 TCP 重传和请求重试的效果。这一步能暴露很多在理想网络下根本不会出现的问题。带宽受限模拟 30Kbps 到 200Kbps 的低带宽测试数据压缩和大包拆分策略。高德在内部搭建的弱网测试环境会把这些参数组合成不同的“弱网等级”从轻度劣化到完全不可用分为多档每一档对应一套客户端预期表现。每次网络优化版本上线前所有核心业务都必须跑一遍全档位的弱网测试不达预期不允许发布。5. 地图数据请求的专项优化瓦片、路况与分级调度5.1 地图瓦片加载预加载和双层缓存是命根子地图瓦片是地图应用最典型、也是量最大的请求类型。一张瓦片是一张 256×256 像素的小图片常见体积在 10KB 到 50KB 之间。用户缩放、平移一次地图可能需要加载几十张瓦片。瓦片请求的体验好坏直接决定地图的“跟手程度”。瓦片优化的核心我总结为三个关键词分级预加载、内存缓存、磁盘缓存。分级预加载的逻辑是用户看到当前视野时App 除了加载当前层级的瓦片还会预测用户下一步可能缩放到的层级提前把低一级和高一级的部分瓦片拉下来缓存。这个预测不是猜而是根据手势方向、地图中心点移动速度、当前缩放级别建立的模型。比如用户在地图上快速向上滑动系统会预加载上方区域的瓦片用户双击某个点进行放大系统会提前加载目标层级中心附近的瓦片。内存缓存用的是 LRU 策略最近使用的瓦片常驻内存保证用户来回拖动地图时能秒级复用。内存缓存的容量要控制好太大会挤压其他业务的内存空间太小则命中率上不去。这块的调优是一个持续的平衡过程我的经验是按单张瓦片平均 20KB 估算内存缓存设置在 50 到 80MB 范围基本能覆盖核心城市一个区域的高频回看需求。磁盘缓存主要解决冷启动后首次打开地图的加载问题。地图瓦片有很强的时效性道路会变、楼宇会变磁盘缓存不能无限期保留。我们的策略是分区管理基础底图数据缓存周期长比如 7 天动态标注数据缓存周期短比如 1 天。缓存命中后要异步验证版本发现内容过期再重新拉取而不是先等验证再显示。5.2 路况数据的时效性与降级方案路况是地图应用里比较特殊的请求数据量不大但对时效性要求极高。一条路况信息超过 5 分钟基本就失去参考价值了。正常网络下路况请求是轻量高频的30 秒刷新一次。弱网下路况刷新就会遇到矛盾刷新太频繁请求发不出去刷新太慢用户看到的是过期路况。我们最终采用的方案是“自适应刷新间隔”网络质量好的时候 15 秒刷一次网络变差时自动拉长到 60 秒一次。同时在弱网下自动切换为粗粒度路况请求只拉取用户当前路线前后几公里的路况概要不再拉全城路况。当然路况数据的传输方式也值得考虑。基于长连接的推送在弱网环境下其实不如“短轮询缓存兜底”稳定。长连接在丢包环境下频繁断开重连开销反而更大。我们的实践是存续的网络连接上使用推送断线时自动回退到定时轮询保证用户始终能拿到至少一份可用的路况数据。5.3 请求优先级调度把最关键的请求插到最前面地图请求的特点是并发度极高、类型复杂。试想一个典型场景用户在导航过程中屏幕上同时需要路况刷新、语音包下载、实时位置上传、路口放大图请求、ETC 记录上报。如果不做优先级调度带宽和连接资源会被大量非关键请求占用直接拖累导航主流程。我们建立了一套客户端请求调度系统核心逻辑是所有网络请求在发出前先经过一个优先级队列。P0 级用户正在进行的核心操作请求比如一次点击“开始导航”后触发的路径规划。P1 级支撑当前界面显示的必要请求比如正在浏览区域的瓦片图、导航中下一路口的示意图。P2 级前体验较好的增强请求比如周边餐饮 POI、路况详情报。P3 级后台预取和上报类任务比如本地数据增量更新、日志上传。调度器会根据当前网络质量动态调整各级请求的并发配额。网络好的时候P2、P3 级请求可以并行跑弱网环境下直接砍掉 P3限制 P2把大部分带宽和连接资源留给 P0 和 P1。这套机制上线后弱网导航场景的卡顿明显减少因为“关键请求等不到资源”的基本矛盾解决了。6. 监控、告警与持续迭代把优化建立在数据之上6.1 客户端网络指标采集不只看平均更要看分位数所有优化上线后都要有数据来验证效果否则就是拍脑袋。我们在客户端埋了一套网络监控指标覆盖全链路的每个步骤。取样的指标包括DNS 耗时分成功和失败统计记录解析耗时分布。连接建立耗时TCP 握手时间、TLS 握手时间分别记录方便定位是网络问题还是证书问题。首包时间TTFB请求发出到收到响应第一个字节的时间这个指标最能直接反映用户等待感受。请求完成率按不同弱网等级统计重点看 4G 弱覆盖和 Wi-Fi 高干扰场景下的表现。客户端错误分布超时、连接重置、DNS 解析失败、TLS 握手失败等错误类型分别统计。这里有一个很容易犯的统计误区只看平均耗时。平均耗时会被极端值拉偏线上某个弱网场景的请求 30 秒才超时平均下来所有请求的耗时都被拉高了但实际上 95% 的请求都很快。我们的监控报表核心看 P50、P90、P95、P99 四个分位以及错误率。弱网优化做得好不好看 P95 比看平均数可靠得多。6.2 上行归因和灰度验证指标采集只是第一步归因才是关键。用户在哪个城市、用的哪家运营商、连的 4G 还是 5G、当时网络质量几分、App 版本是多少——这些信息要和网络指标一起上报才能在问题发生时快速定位。比如某个版本上线后上海电信用户的 DNS 解析成功率明显下降如果监控只看到“DNS 失败率升高”而没有细分维度排查起来会非常痛苦。灰度发布也是网络优化必须遵守的流程。网络相关的改动风险往往比业务功能改动更隐蔽——它受环境影响大同一个改动在办公网下表现很好到了弱网环境可能完全是另一回事。所以我们的节奏是配置中心动态下发首批 5% 白名单用户验证核心指标无劣化再逐步放量到 30%、100%。这个放量过程一般需要 2 到 3 个自然日不是技术婆婆妈妈而是留够时间观察长尾场景的网络表现。6.3 实际调优中容易被忽略的细节最后分享几个自己在项目里被反复磨炼出来的细节这些写在标准文档里都容易被跳过千万不要高估客户端的本地时间。埋点数据里的时间戳最好由服务器在响应头里下发时间校准否则客户端本地时间乱了所有耗时分析都没有参考意义。日志上报要有采样率。全量上报网络日志流量消耗反而是个问题。我们的策略是正常网络下 5% 采样弱网或请求失败时 100% 采样。这样监控覆盖和流量成本能形成一个相对合理的平衡。网络优化和服务端优化必须联动。有段时间我们盯客户端调优怎么调首包时间都压不下来后来排查发现是服务端业务逻辑里有一个耗时的同步调用纯粹拖了响应后腿。客户端把问题定位到了还要推动服务端一起改。全链路优化链路两端的配合不能少。别忘了“无网”场景。弱网优化做得再细也要给离线场景留好退路。高德离线地图的存在本身就是一种网络优化思路——与其在弱网环境下挣扎不如把一部分数据直接搬进用户手机。实际导航过程中离线数据和在线数据的无缝切换用户是感知不到的但体验差异巨大。做了这么多年移动端网络优化我最大的体会是网络问题永远不会被彻底消灭。你优化了 DNS还会遇到连接被重置你优化了连接池还会遇到链路丢包你优化了超时重试还会遇到服务端抖动。真正让用户觉得“这个 App 网络挺好的”不是因为你解决了一切问题而是因为在任何糟糕的网络条件下用户的核心任务总是能完成。哪怕慢一点哪怕图像质量低一点只要用户还能被带到目的地这场优化就算赢了一半。这些经验希望能给正在做网络优化的你一些参考尤其是里面的超时分级、弱网降级、优先级调度这几个思路换个业务场景同样适用。