【免费下载链接】monogram
Native Telegram client for Android
Monogram 是一款用 Kotlin + Rust 构建的原生 Android Telegram 客户端,其媒体下载架构是整个项目工程亮点之一:从 Kotlin 侧的优先级队列,到 Rust 侧的分片下载管线,再到跨数据中心的自动迁移与 CDN 加密加速,Monogram 用一套「不丢字节、可断点续传、受控并发」的设计,让图片和视频下载在弱网环境下依然稳定高效。本文将带你完整看懂这套 Monogram 媒体下载架构。
一张图看懂整体分层
Monogram 的下载链路横跨两个模块,职责边界非常清晰:
| 层 | 模块 | 职责 |
|---|---|---|
| 调度层(Kotlin) | network/http/ | 优先级队列、缓存、流式前缀、进度回调 |
| 协议层(Rust) | native/mtproto-rs/ | 分片管线、断点续传、跨 DC、CDN、限流 |
Kotlin 层决定「下载什么、按什么顺序下载」,Rust 层决定「怎么把一个文件最快最稳地取回来」。所有协议调用都经过 network/bridge/ 边界,UI 永远不会直接接触原始 MTProto 错误。
优先级队列:谁的图先下?
调度核心是 MediaRepository.kt。它为每个媒体 key 维护一个去重任务(TelegramJob):同一条消息的图片被聊天列表、预览页同时请求时,只产生一次真实传输,多个「消费者」共享同一任务并共享进度。
六个优先级档位
MediaPriority.kt 定义了从IDLE(0)到USER(30)的六个档位:
- IDLE / DEFAULT:预取、后台补缓存
- VISIBLE / DISPLAY / THUMB:屏幕可见的缩略图与聊天气泡图
- USER:用户显式点击下载,最高优先级
队列是「大顶堆 + 序号」:高优先级先跑,同优先级先进先出。若一个正在运行的低优先级任务遇到更高优先级的消费者,调度器会通过promote通知 Rust 侧把传输窗口升级——正在下载的图「越用越急,越快越快」。
双队列工作池
MediaRepository的 companion 常量 揭示了工作池规模:
TELEGRAM_WORKERS = 5 // 全尺寸媒体并发 THUMB_WORKERS = 2 // 缩略图专用通道 MAX_LARGE_PIPELINES = 2 // 大文件(≥20MB)并发上限取任务的pollTelegramLocked还有两个「让位」规则:队列里存在DISPLAY级以上任务时,低优先级任务暂缓;存在非 IDLE 任务时,IDLE 任务让路。缩略图走独立工作线程,永远不会被大图下载「卡住」。
分片管线:一次请求 8 个分片
真正取字节的是 Rust 侧的download_media_range_batched_streaming_capped。它维护一个「飞行中窗口」:同一时刻有 N 个分片请求在途,每返回一个就立即补发一个新偏移,连接始终保持满载,而不是每个分片等一个完整往返。
按场景选择下载配置
download_window 按「下载画像」给出分片大小与并发数:
| 画像 | 触发场景 | 分片大小 | 在途分片 |
|---|---|---|---|
| Thumb | 缩略图 | 128 KiB | 1 |
| Ordinary / Background | 普通 / 后台预取 | 128 KiB | 4 |
| Visible / Playing / User | 可见图、播放中视频、手动下载 | 512 KiB | 8 |
所有分片大小都满足 Telegram 的约束:偏移和长度必须是 4 KiB 的倍数,单次请求不跨越 1 MiB 边界(完整规则见 ARCHITECTURE.md 的 Downloads 章节)。
遇到LIMIT_INVALID错误时,limit_invalid_window会自动降档——分片缩小到 128 KiB、在途数减半,然后整窗重试。超时错误则最多从「第一个缺失偏移」重发 3 次,已落盘的字节不会浪费。
断点续传:三个伴生文件的设计
这是 Monogram 下载架构中最精巧的部分。每个下载任务写入的不是一个文件,而是「三件套」:
| 文件 | 作用 |
|---|---|
<dest>.<pid>.<seq>.part | 暂存文件,字节按各自偏移 seek 写入,乱序到达也正确 |
<dest>.ranges.part | 缺失区间清单(MissingRanges),每写完一段就更新并落盘 |
<dest>.identity.part | 文件身份:file_id、access_hash、DC、大小、版本 |
实现见 MissingRanges 与 StagedDownload。中断后重启时:
- 读取
.ranges.part还原「哪些洞没填」,resume_start直接跳到第一个洞,已完成字节一个不重下; - 比对
.identity.part——如果文件已被服务器端更新(file_id/大小变化),整个暂存作废重来,旧版本字节绝不会被拼进新文件; - 同一目标路径有
DestinationLock串行保护,杜绝并发写坏。
哪些错误值得保留暂存文件?keep_partial给出了白名单:FLOOD_WAIT、超时、FILE_REFERENCE、AUTH_KEY_*、网络/传输类错误都保留;取消也保留(对应 Androidcancel(false)语义),下次可直接续传。清理方面,staging 清扫 会删除 2 分钟未更新、总量超过 32 MiB 的过期暂存,最老的先删。
跨数据中心:FILE_MIGRATE 自动迁移
Telegram 的文件常常存放在与账号不同的数据中心。Monogram 的处理策略是「只迁媒体 DC,不动主会话」:
- 首次
upload.getFile返回FILE_MIGRATE_X时,file_migrate_dc解析目标 DC(最多重试 3 次防循环),download_media_range_on_lane 把 DC 改写后重新准入并续传——注意USER_MIGRATE这类账号级错误不会触发迁移; - 跨 DC 需要独立的认证密钥。prepare_media_lane_snapshot 决定三条路径:
Reuse(同 DC 复用连接)、Replaced(同 DC 换新 session)、NeedExport(通过exportAuthorization把密钥导出到媒体 DC),全程走专用「媒体车道」,绝不占用主 RPC 套接字; - 准备第二条车道时还有 MediaLanePreparation:在短命线程里提前建立第二条 TCP 并绑定临时密钥,与第一个分片窗口并行,把「开连接 + 认证」的开销藏进首包延迟里。
CDN 加速:加密传输 + 双重校验
当主 DC 判定文件适合 CDN 分发时,会返回fileCdnRedirect(携带 CDN DC、file_token、AES 密钥)。Monogram 的处理见 follow_cdn_redirect:
- 取公钥:
help.getCdnConfig拉取 CDN 配置,找到目标 DC 的公钥,用 Diffie-Hellman 建立独立会话; - DNS-0202 发现节点:dns_txt.rs 解析
cdn-{dc}.telegram.org的 TXT 记录拿到节点地址与 secret,经 MTProxy 混淆连接; - 逐分片拉取:
upload.getCdnFile按 128 KiB 分片取数;若 CDN 返回「需要重传」,用主会话调upload.reuploadCdnFile补登记,最多 3 次; - 解密:
decrypt_cdn_part用 AES-256-CTR 逐分片解密,IV 的低 4 字节由偏移量推导,因此任意分片可以独立解密——这正是断点续传能用在 CDN 路径上的前提; - 校验:
verify_cdn_part用服务器下发的 SHA-256 哈希表逐段比对,任何一段不符立即中止,「Fail closed」原则贯穿始终; - 降级兜底:
cdn_or_origin在 CDN 路径失败后自动回落到主 DC 直连,用户无感知。
一个细节:只有offset == 0才允许走 CDN(见 follow_cdn_redirect 开头的检查),从中间偏移恢复的文件仍由源 DC 下载,规避了 CDN 分片哈希的覆盖范围问题。
并发控制与 FLOOD_WAIT 退避
并发控制分两级,全部在 transfer_policy.rs 中:
准入控制(每 DC 限流)
admit_download按「(DC, 是否大文件)」维度放行,默认限额是 queue_limits = (5, 2)——普通下载同 DC 最多 5 个在途,大文件(≥20 MiB)最多 2 个。等待队列按「优先级 + 排队时长」排序,order_at 让长时间排队的高优请求逐渐「升值」,但永远插不到写请求前面——防止后台预取饿死前台。
服务端 FLOOD_WAIT 退避
FloodScope为每个 (DC, 方向) 维护退避状态:
- 收到
FLOOD_WAIT_n后,窗口缩小(在途数减半、最低 1),并在服务器指定的秒数内拒绝新请求; - Premium 会话收到
FLOOD_PREMIUM_WAIT时退避更彻底(在途降到 1); - 退避结束后进入「恢复期」:每连续成功 32 个分片且间隔 30 秒,才把并发度加回 1,逐步爬回满速,避免立刻再被限流。
这套「先刹车、再缓放」的策略,是长时间弱网下载不反复触发限流的关键。
缓存、进度与流式前缀
Kotlin 侧的 FileCache 以内容 key 管理缓存文件,支持按聊天/类型统计与清理;显式「下载」的文件通过 keptDownload 复制到downloads/目录并pin住,清缓存不会误删。
针对「点开视频立即能播」,ensureStreamingPrefix 只预取前2 MiB(切成 4 个 512 KiB 分片,STREAM_PART),而不下载整个文件;视频还会进一步预取 MP4 moov 元数据,让播放器拿到时长、分辨率等关键信息。播放中的请求按Playing画像获得 512 KiB × 8 的最宽窗口,拖动进度条时 cancel_outside_seek_window 会把远离播放窗口的在途请求取消,带宽永远跟着播放头走。
进度上报由 Rust 的 notify_progress 回调到 Kotlin 的 onNativeProgress,最终汇入downloadProgress状态流驱动 UI 进度条。
架构小结
| 能力 | 关键机制 | 代码位置 |
|---|---|---|
| 任务去重与优先级 | TelegramJob共享消费者 + 双工作池 | MediaRepository.kt |
| 分片管线 | 在途窗口 + 滑动补发 + 自动降档 | download.rs |
| 断点续传 | .part+.ranges.part+.identity.part三件套 | download.rs |
| 跨 DC 迁移 | FILE_MIGRATE_X+exportAuthorization媒体车道 | media_download.rs |
| CDN 加速 | fileCdnRedirect+ AES-256-CTR + SHA-256 逐段校验 | cdn.rs |
| 并发准入 | (DC, 大文件) 维度限额 + 优先级老化 | transfer_policy.rs |
| 限流退避 | FLOOD_WAIT 窗口收缩 + 32 次成功阶梯恢复 | download.rs |
Monogram 的媒体下载架构展示了教科书级的取舍:不追求单文件极限并发,而是用「优先级画像 + 准入控制 + 渐进恢复」平衡吞吐与稳定性;用三个伴生文件把「断点续传」做到字节级精确且身份安全;用「先试 CDN、失败回落源 DC」把加速做成无感知的增强而非硬依赖。如果你想深入细节,建议从 ARCHITECTURE.md 的 Downloads 章节读起,再对照 mtproto-rs 的单元测试(如media_download_tests、cdn_transfer_tests、staging_sweep_tests)——每个机制背后都有对应的测试用例可以验证理解。
【免费下载链接】monogram
Native Telegram client for Android
相关推荐
HMCL启动器下载加速:多源下载与断点续传
HMCL启动器下载加速:多源下载与断点续传 Minecraft玩家常面临游戏资源下载慢、中断后需重新下载的问题。HMCL(Hello Minecraft! La
桌面应用游戏开发curl下载管理:断点续传、速度限制、进度监控
curl下载管理:断点续传、速度限制、进度监控 概述 在日常开发和系统管理中,文件下载是常见需求。curl作为强大的命令行传输工具,提供了丰富的下载管理功能,包
CLI网络Aerial视频下载加速:多线程下载与断点续传实现
Aerial视频下载加速:多线程下载与断点续传实现 你是否还在为Aerial视频下载速度慢而烦恼?是否遇到过下载中断后需要重新开始的尴尬?本文将深入解析Aeri
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考