Skip to content

AOT Compile and Runtime zh

ddddd edited this page Jun 30, 2026 · 3 revisions

AOT 编译与运行时

状态

本页记录 SPACK 当前的 compiler/runtime 拆分,以及后续仍要推进的设计方向。

从 v2.0.3 开始,SPACK 发布独立的 runtime 和 compiler 产物:

  • spack-runtime 可以服务普通前端 dist 目录,也可以服务 compiler 生成的 .spack bundle。
  • spack-compiler 负责构建期优化并写入 .spack bundle。
  • 压缩和图片生成属于 compiler 路径,不属于生产 runtime 路径。
  • runtime 不需要 libvips。

目标

SPACK 应该把前端构建产物编译成不可变、预优化的 .spack 文件,然后由极简 runtime 提供 HTTP 静态资源交付。

前端构建产物
-> spack-compiler compile ./dist -o app.spack
-> spack-compiler verify app.spack
-> spack-runtime serve ./app.spack
-> HTTP 静态资源交付

为了兼容和简化迁移,直接目录服务能力仍然保留:

前端构建产物
-> spack-runtime serve ./dist
-> 不经过 AOT 预优化的 HTTP 静态资源交付

compiler 可以比较重。runtime 应该保持很小。

发布产物

产物 职责
spack-compiler 构建期工具。扫描资源、生成变体、写入 .spack、校验并输出报告。
spack-runtime 生产 runtime。加载 .spack 或服务本地目录,选择响应计划并发送 payload。
spack runtime 镜像 默认 Debian slim runtime 镜像。
spack:alpine runtime 镜像 Alpine runtime flavor。
spack-compiler 镜像 包含 libvips 运行依赖的 Debian slim compiler 镜像。

为什么拆分 compiler 和 runtime

静态资源交付 runtime 不应该在生产环境携带构建期图片和压缩工具链。

AOT 模型把确定性工作移动到构建期。生产环境只保留数据面:

  • 不包含运行时图片流水线。
  • 不包含运行时压缩流水线。
  • bundle 模式不需要 source watcher。
  • bundle 模式不需要 artifact cache janitor。
  • 请求路径不会每次解压 .spack。

.spack 文件模型

当前 bundle 使用 zstd 作为 bundle 级压缩,并包含 SPACK metadata 供 runtime 加载和校验。

app.spack

bundle header
metadata / index
compressed payload section
verification data

关键 runtime 契约不是内部容器实现,而是:

  • 相同输入和配置下,compiler 写出的 bundle 应该可复现。
  • bundle 发布前可以由 compiler 校验。
  • runtime 启动时校验 bundle。
  • runtime 启动时只解压一次 bundle 到受管临时目录。
  • 请求从解压后的本地文件服务,不会每次请求都解压 .spack。

启动解压可以保持 runtime 简单,并保留高效的本地文件服务行为,同时避免把整个 bundle payload 常驻内存。

Bundle 规则

compiler 必须强制执行这些规则:

  • entry path 使用 UTF-8,并统一为 / 分隔。
  • 拒绝绝对路径。
  • 拒绝 .. 路径逃逸。
  • 拒绝反斜杠路径。
  • 拒绝符号链接。
  • SPACK metadata 路径为保留路径。
  • entry 按稳定顺序排序。
  • 时间戳由 reproducible 模式或 SOURCE_DATE_EPOCH 固定。
  • 文件权限固定。
  • bundle 校验必须拒绝 malformed metadata、非法路径和截断 payload。

Runtime 索引

runtime 不应该在每次请求时扫描源目录或 bundle entry。

索引应该包含:

  • 字符串表。
  • 路由表。
  • 响应表。
  • Header 表。
  • 变体表。
  • Blob 表。
  • 完整性元数据。

响应指向 compiler 准备好的 payload,或者直接目录 catalog 中的 payload。

route path
-> route id
-> selected response id
-> selected encoding variant
-> extracted local file 或 direct source file
-> payload delivery

可以做的预优化

compiler 可以提前完成所有不依赖单个请求的工作。

路由索引

compiler 负责路径归一化、重复路径检测、大小写冲突检测、SPA fallback 目标校验,并写入路由记录。

runtime 只做路由查找。

响应计划

compiler 写入响应记录,包括:

  • 状态码。
  • Content-Type。
  • Content-Length。
  • ETag。
  • Last-Modified 策略。
  • Cache-Control。
  • Vary。
  • Link 资源提示。
  • 安全响应头。
  • Payload 引用。

runtime 只需要选择响应计划并写出。

压缩变体

compiler 根据预算和收益率生成 Brotli、Gzip、Zstd 变体。

runtime 根据 Accept-Encoding 查表选择变体。

图片变体

图片生成属于 compiler。

compiler 使用支持 libvips 的构建或镜像生成不同宽度和格式的图片变体。runtime 不需要 libvips。

常用矩阵参数是 --image.enable、--image.widths 和 --image.formats。发布校验和 Refine AOT benchmark 会通过 SPACK_IMAGE_ENABLE、SPACK_IMAGE_WIDTHS、SPACK_IMAGE_FORMATS 透传同一组设置。

compiler 应该强制执行:

  • 不放大原图。
  • 最大源文件字节数。
  • 最大解码像素数。
  • 单资源最大变体数量。
  • 最大 bundle 膨胀比例。
  • 最小收益率。
  • 图片格式能力检测。

Blob 去重

当 bundle layout 支持时,compiler 对 payload 做 hash,相同内容只保存一次。

去重范围包括原始文件、压缩变体、图片变体和源目录中的重复文件。

归档布局

compiler 应该把首屏热资源放在相邻位置:

  • index.html。
  • 入口 CSS。
  • 入口 JavaScript。
  • Vendor JavaScript。
  • 关键字体。
  • 关键图片。

大下载文件、source map 和冷资源可以放在后面。

HTML 和 Manifest 分析

compiler 可以分析 index.html、Vite manifest、Webpack manifest、Rspack manifest 和自定义资源清单。

它可以校验缺失引用,并生成 resource hints。

V1 应该优先生成 Link 响应头,而不是直接改写 HTML。

条件请求和 Range 元数据

compiler 提前准备 ETag、HEAD、304、Range、206、416 所需的元数据。

Range 请求优先使用 identity payload,不对预压缩变体做字节范围交付。

Runtime 职责

runtime 应该负责:

  • 打开 .spack 或本地源目录。
  • 启动时校验 bundle metadata 和边界。
  • 启动时只解压一次 .spack 到受管临时目录。
  • 构建或加载路由查找结构。
  • 选择响应计划。
  • 服务解压后的本地文件或直接源文件。
  • 提供健康检查、指标、日志和优雅关闭。

默认 runtime 不应该包含:

  • libvips。
  • 运行时图片流水线。
  • 运行时压缩流水线。
  • bundle 模式的 source watcher。
  • bundle 模式的 catalog rescan。
  • bundle 模式的 artifact janitor。
  • bundle 模式的动态 artifact cache。
  • bundle 模式的运行时源文件 hash。

Compiler 报告和 source sidecar 信任策略

从 v2.0.5 之后的下一次发布开始,spack-compiler compile 支持可选 JSON 报告:

spack-compiler compile ./dist -o app.spack --report compile-report.json

报告会记录:

  • 输入 asset 数量。
  • variant 数量。
  • source sidecar 数量。
  • 源文件总字节数。
  • bundle 文件字节数。
  • 编译耗时。
  • 压缩模式。

前端构建产物里的外部压缩 sidecar 默认不直接信任。SPACK 会同时校验 HTTP content coding,以及二进制图片资源解码后的 magic。例如 hero.png.gz 必须是合法 gzip 流,并且解码后必须是 PNG 字节,才会被注册为 source sidecar variant。无效 sidecar 会被忽略,runtime 会 fallback 到真实源文件。

校验与检查

spack-compiler verify app.spack 应该支持完整校验。

spack-compiler decompile app.spack 应该支持为 CI 和调试提取或检查 bundle 内容。

spack-runtime serve app.spack 应该支持快速启动校验。

校验范围包括:

  • Bundle path 规则。
  • Metadata 格式版本。
  • Section 边界。
  • Route 引用。
  • Response 引用。
  • Blob 引用。
  • Payload hash。
  • Bundle hash。

实现状态

能力 状态
runtime 直接目录服务 已支持。
compiler/runtime 拆分 已支持。
.spack bundle 编译 已支持。
.spack 校验 已支持。
bundle 启动解压 已支持。
runtime 每请求解压 不使用。
runtime libvips 依赖 不需要。
compiler libvips 依赖 compiler 镜像需要。
manifest 分析和高级 layout 优化 后续工作。
签名、overlay、mmap 和 offset sendfile 后续工作。

成功标准

生产可用的 AOT 路径应该满足:

  • 相同输入和配置下,spack-compiler compile ./dist -o app.spack 产物可复现。
  • 部署前 spack-compiler verify app.spack 通过。
  • spack-runtime serve ./app.spack 不扫描源目录即可服务资源。
  • runtime 启动不执行压缩和图片生成。
  • 请求不会重复解压 bundle。
  • ETag、HEAD、304、Range、SPA fallback 和压缩变体选择都来自准备好的 metadata。
  • 生产 runtime 不依赖 libvips。
  • runtime 镜像明显小于 compiler 镜像。
  • 热路径性能不低于 direct prepared filesystem mode。

Clone this wiki locally