给 JVM 的多线程下载引擎

TurboDL 是一个用纯 Kotlin 从零写的下载内核:按 HTTP Range 分片并发、可断点续传,并把「不是 HTTP 的东西」做成插件框架。不依赖 Android,任何 JVM 应用都能直接用。

v0.2.2.0
运行要求
JVM 17+
开源协议
MIT
实测环境
258 个用例,0 跳过,0 失败

它能做什么

下载引擎真正要紧的六件事 —— 其余在 README 里。

分片并发下载

按 HTTP Range 并行下载分片,并复用连接(HTTP/2 多路复用 + keep-alive)。

动态分片 + 工作窃取

细粒度预分片 + 工作窃取:一条慢连接拖不垮整个传输,也消除了「最后一个分片单线程收尾」的长尾。

克制的自适应

**只在**遇到 429/503 或连续失败时才降低并发。普通抖动绝不改线程数 —— 不做 AIMD 来回震荡。

按真实进度续传

分片状态落盘;暂停/继续按磁盘上真实写完的字节接着下,而不是按记忆中的数字。

拒绝 Range 篡改

会核对服务端实际返回的字节是否就是请求的那段 Range —— 忽略 Range 直接回整文件的服务端会被识别出来,而不是把错位数据写进文件。

字节级完整性校验

合并完成后校验总大小;少了或多了都判为失败。

九项引擎能力

每一项都是公开的配置字段 —— 没有藏着的手法。

能力配置项说明
全局限速globalSpeedLimitBytesPerSec0 = 不限速
线程数maxConnectionsPerTask1..256
并发任务数maxConcurrentTasks1..64
最大重试maxRetries0..50
动态分片dynamicSegmentationtrue / false
代理proxy直连 / 系统 / 手动(HTTP,SOCKS,鉴权) / PAC
DNSdns系统 / 静态 hosts / DoH(url)
忽略 SSLtrustAllCerts抓包调试用
主题—由上层应用负责;SDK 不涉及界面

模块构成

只有 turbodl-core 是必需的。其余都是你主动加进来的插件。

构件作用必需?
turbodl-core下载引擎本体
分片并发下载、断点续传、重试、限速、DNS/代理。可单独使用,**不依赖**插件框架。
必需
turbo-plugin-runtime插件运行时内核
生命周期、清理(disposer)、事件总线、服务注册表、扩展点、版本握手、诊断。对任何具体协议一无所知。
可选
turbo-plugin-bootstrap基础装配
一次调用装配基础插件(Kotlin 加载器 + HTTP 后端),并桥接引擎的事件流。
可选
turbo-plugin-hlsHLS VOD 适配
解析 M3U8 清单、分片并发下载(分片级重试)、AES-128 解密、支持 EXT-X-BYTERANGE。直播/DRM/fMP4 清单会被**明确拒绝**,而不是产出一个损坏的文件。
可选
turbo-plugin-jsJavaScript 插件加载器
加载用 JavaScript 写的 TurboDL 插件。内嵌 QuickJS —— 每个脚本一个 runtime 与 context —— 并给脚本一套稳定的 `host`/`plugin` ABI。
可选
turbodl-cli命令行工具
基于 SDK 的完整命令行工具:多线程、断点续传、批量、给脚本与 Agent 用的 NDJSON 输出。以 fat JAR 形式提供。
可选

快速开始

加一个依赖、提交一个请求、订阅事件。完整的 API(暂停 / 继续 / 取消 / 批量 / 诊断)见文档。

Kotlin
val client = TurboClient(
    TurboConfig(
        maxConnectionsPerTask = 16,      // up to 256 / 最高 256
        globalSpeedLimitBytesPerSec = 0, // 0 = unlimited / 不限速
        dynamicSegmentation = true,
    )
)

val id = client.submit(
    DownloadRequest(url = "https://example.com/big.zip", destination = File("big.zip"))
)

client.events.collect { event ->
    when (event) {
        is TurboEvent.Progress  -> println("${event.progress.percent}%")
        is TurboEvent.Completed -> println("done: ${event.file}")
        is TurboEvent.Failed    -> println("failed: ${event.reason}")
        else -> {}
    }
}

val result = client.await(id)   // Result<File>
client.shutdown()

插件框架

TurboDL 既是独立的引擎,也是可选的插件平台。内核不认识任何协议:它只提供生命周期、清理、事件总线、服务注册表、扩展点、版本握手与诊断 —— 全部就这些。

Kotlin · bootstrap
val boot = TurboBootstrap.create(
    config = TurboConfig(maxConnectionsPerTask = 16),
    extraPlugins = listOf(HlsPlugin()),
)

boot.host.loadSource(
    PluginSource(kind = "js", uri = "/opt/turbodl-plugins/acme-parser.js")
)

写在文档里的东西

读者会想知道的每一件事 —— 插件怎么写、清单有哪些必填项、会连带引入哪些库、什么数据会离开你的机器、协议究竟写了什么 —— 在这里是一页,在仓库里是一个文件。两处由同一份源渲染,不会各说各话。