- 后端
- Web框架
【免费下载链接】egg
🥚🥚🥚🥚 Born to build better enterprise frameworks and apps with Node.js & Koa. https://307.run/eggcode
本文介绍 Egg 框架基于 Node.js V8 Startup Snapshot 的启动加速方案:在构建期把完全加载好的应用(框架元数据、插件、Service、Router、tegg 模块)固化成可序列化的堆快照(blob),恢复时跳过绝大部分启动开销,直接续跑剩余生命周期并监听端口。读完本文,你将掌握egg-bin snapshot build构建、egg-scripts start --snapshot-blob恢复的完整命令行流程,理解buildSnapshot/restoreSnapshot对外 API 与四个快照生命周期钩子的用法,并具备定位"哪个模块不可快照化"以及修复构建/恢复失败的实战能力。
什么是 V8 启动快照
Egg 可以把一个完全加载好的应用固化成 V8 启动快照,从而让冷启动跳过绝大部分启动开销。构建快照时会加载整个模块图——框架元数据、插件、Service、Router 和 tegg 模块——并把生命周期跑到configWillLoad,再把此时的堆序列化成一个 blob。恢复 blob 时只需继续执行剩余生命周期(didReady)并开始监听,进程几乎可以立即对外服务。
它构建在 Bundle 部署 之上:快照是从单文件自包含 bundle 产出的,因此可以把快照理解为「预启动好的 bundle」。
从仓库源码看,这个设计贯穿了三个包的分工:
- 构建期:
egg-bin的snapshot build命令(tools/egg-bin/src/commands/snapshot.ts)以快照模式打包应用,然后包裹执行node --snapshot-blob <blob> --build-snapshot worker.js; - 打包期:
@eggjs/egg-bundler保持网络栈 external 且惰性加载(见 tools/egg-bundler/src/lib/Bundler.ts 中snapshotLazyModules相关逻辑); - 恢复期:
egg-scripts start --snapshot-blob以单进程方式从 blob 启动(见 tools/scripts/src/commands/start.ts)。
Node.js 版本要求
| 阶段 | 命令 | Node.js |
|---|---|---|
| 构建快照 | egg-bin snapshot build | >= 22 |
| 恢复(运行) | egg-scripts start --snapshot-blob | >= 24 |
::: warning 恢复必须使用 Node.js >= 24 快照可以在 Node.js >= 22 上构建,但在 Node.js 22 上恢复一个非平凡的 Egg 堆时,进程会在反序列化阶段以原生 fatal 错误崩溃(Check failed: current == end_slot_index,属于 V8 的 bug)。请始终在 Node.js>= 24上恢复。
受支持的启动方式会强制拦截:egg-scripts start --snapshot-blob在 Node.js < 24 时会在启动任何进程前直接报清晰错误并拒绝启动。如果你绕过它、在 Node.js 22 上直接运行node --snapshot-blob,进程仍会在反序列化阶段以上面的原生 fatal 崩溃——快照自带的运行时拦截只能在「能完成反序列化但仍低于 24」的版本上打印友好提示。因此请始终通过egg-scripts在 Node.js >= 24 上恢复。 :::
这条版本约束在源码中可以得到印证:egg-scripts的start命令会在 spawn 之前探测实际执行的 Node 主版本,若小于 24 则直接抛出egg-scripts start --snapshot-blob requires Node.js >= 24 to restore a V8 snapshot错误(见 tools/scripts/src/commands/start.ts)。而egg-bin snapshot build在构建成功后也会打印提示:恢复该快照需要 Node.js >= 24(见 tools/egg-bin/src/commands/snapshot.ts)。
使用 CLI 构建与恢复
推荐使用 bundler CLI。注意没有egg-bin snapshot start:构建属于构建期(egg-bin),恢复属于生产运行期(egg-scripts)。
构建 blob
# 以快照模式打包(单文件自包含 worker.js + prelude),并自动执行 # `node --snapshot-blob <blob> --build-snapshot worker.js` $ egg-bin snapshot build默认会把 bundle 写到./dist-bundle,blob 写到./dist-bundle/snapshot.blob。常用参数:
| 参数 | 说明 |
|---|---|
--output <dir> | bundle 输出目录(worker.js所在目录)。 |
--blob <path> | 快照 blob 路径,默认<output>/snapshot.blob。 |
--force-external | 始终保持 external 的包,可重复(见「已知限制」)。 |
--skip-bundle | 从已有的worker.js构建 blob(跳过打包)。 |
--inline-external <pkg> | 把被自动 externalize 的包强制塞回 bundle,可重复。 |
--pack-alias <spec>=<target> | 打包期重定向某个模块说明符,可重复。 |
--dry-run | 只打印将被执行的node --build-snapshot命令,不实际 spawn。 |
--mode <mode> | bundle 构建模式,production(默认)或development。 |
--framework <pkg> | 框架包名,默认egg(或读取pkg.egg.framework)。 |
以上标志的默认值与含义均与 tools/egg-bin/src/commands/snapshot.ts 中的 oclif flag 定义一一对应:--output默认./dist-bundle,--blob默认<output>/snapshot.blob,--mode默认production。
构建流程在源码中分为三步(见 tools/egg-bin/src/commands/snapshot.ts):
- 若非
--skip-bundle,调用@eggjs/egg-bundler的bundle()以snapshot: true模式打包,产出单文件自包含worker.js与 prelude; - 以继承 stdio 的方式 spawn
node --snapshot-blob <blob> --build-snapshot worker.js,并通过环境变量EGG_BUNDLE_SNAPSHOT=build让生成的入口进入"快照构建"模式(加载元数据、执行snapshotWillSerialize钩子、注册 deserialize 主函数); - 构建结束后校验 blob 文件确实存在,否则显式报错
snapshot build finished but no blob was written at <path>(node --build-snapshot可能在没写出 blob 的情况下以 0 退出)。
值得一提的实现细节:该命令用spawn而不是fork来启动子进程,因为 IPC channel 是一种不可序列化的 libuv 资源,会破坏--build-snapshot;同时它会把SIGINT/SIGTERM/SIGQUIT转发给子进程,避免构建中途 Ctrl-C 时留下孤儿进程。
恢复并提供服务
直接用egg-scripts从 blob 启动进程(Node.js >= 24):
$ egg-scripts start --snapshot-blob ./dist-bundle/snapshot.blob --port 7001它会启动一个单文件自包含的node --snapshot-blob <blob>进程(没有 egg-cluster,也不做框架解析)。快照主函数从PORT(或--port)读取监听端口,执行snapshotDidDeserialize钩子,然后调用app.listen()。从源码看(tools/scripts/src/commands/start.ts),snapshot 启动路径与 cluster 路径共享同一套 spawn + daemon/foreground 生命周期管理,只是最终 argv 不同:[..., '--snapshot-blob', blob, '--title=egg-server-<name>-snapshot'],且egg-scripts stop可通过进程名 grep 到它。
对外 API
如果需要自定义入口文件,Egg 也从egg导出两个方法:
import { buildSnapshot, restoreSnapshot } from 'egg';buildSnapshot()会以快照模式启动 Egg,加载元数据,触发非可序列化资源的清理钩子,并把应用对象写入 V8 快照负载。restoreSnapshot()会从快照中恢复应用,重建运行期资源,并继续执行剩余的 Egg 生命周期。
这两个方法的实现位于 packages/egg/src/lib/snapshot.ts:
buildSnapshot(options)(packages/egg/src/lib/snapshot.ts)调用startEgg({ ...options, snapshot: true })以快照模式启动,先触发 agent(若存在)再触发 app 的snapshotWillSerialize,最后通过v8.startupSnapshot.setDeserializeMainFunction()把应用对象注册为反序列化回调负载(存入globalThis.__egg_snapshot_app);restoreSnapshot()(packages/egg/src/lib/snapshot.ts)从globalThis.__egg_snapshot_app取回应用,先触发 agent 再触发 app 的snapshotDidDeserialize钩子,返回已恢复的Application实例。
构建入口:
import { buildSnapshot } from 'egg'; await buildSnapshot({ baseDir: import.meta.dirname, });node --snapshot-blob=snapshot.blob --build-snapshot snapshot-entry.mjs恢复入口(Node.js >= 24):
import { restoreSnapshot } from 'egg'; const app = await restoreSnapshot(); await app.listen(7001);restoreSnapshot()会从configDidLoad继续执行正常启动流程直到didReady,因此返回的app已经可以继续执行运行期初始化逻辑,例如启动服务或建立外部连接。
工作原理
在构建阶段,Egg 会以snapshot: true运行。此时 Egg 会加载应用元数据,但在configWillLoad之后停止,因此configDidLoad、didLoad、willReady、didReady和serverDidReady等钩子都会延后到恢复阶段执行。在序列化堆之前,Egg 会运行snapshotWillSerialize钩子,释放不可序列化的资源(timer、socket、原生句柄、logger 流)。网络栈(node:http、node:https、TLS/DNS、HTTP client)会保持external 且惰性加载,因此它们不可序列化的原生绑定不会被写进 blob,而是在恢复后首次使用时重建。
恢复阶段,V8 先反序列化堆,随后快照主函数运行snapshotDidDeserialize钩子重建这些运行期资源,跑完延后的生命周期直到didReady,然后开始监听。
在打包器层面,这一机制通过三步实现(见 tools/egg-bundler/src/lib/Bundler.ts):
- 强制单文件:V8 启动快照禁止在用户态
require兄弟 chunk,因此快照模式下即使应用配置了多 chunk,也会强制singleFile: true(Bundler.ts中const singleFile = snapshot ? true : mergedPack?.singleFile); - 网络栈保持 external:默认的
snapshotLazyModules覆盖 Node 网络栈 id,并通过externalsMap传给@utoo/pack,避免http/tls/dns被内联后(在快照构建期加载)因不可序列化而失败; - 注入惰性 external 派发:在序列化前读取每个 external 的导出名,把惰性 hook 注入到生成的
externalRequirehelper 体中,并把 prelude 前置到每个入口的worker.js之前。若检测到externalRequire被生成但惰性 hook 注入失败,bundler 会故意 fail closed,而不是产出一个会在构建期加载网络栈的 blob(见 tools/egg-bundler/src/lib/Bundler.ts)。
保持 external 且惰性加载的模块集合默认是 Node 网络栈(http、https、http2、tls、dns、inspector,含它们的node:形式)。如果该列表之外的某个 builtin 在 import 时初始化了原生状态,可以通过应用package.json里的egg.snapshot.lazyModules扩展这个集合:
{ "egg": { "snapshot": { "lazyModules": ["node:zlib"] } } }它会被合并到默认值之上(即默认列表无需自行重复列出),在构建期被打桩、在恢复时被真实加载。
当某个第三方依赖或 builtin 破坏了构建或恢复时,参见 快照故障排查,了解如何定位罪魁祸首模块并修复。
快照生命周期钩子
如果你的app.js或agent.jsBoot 类管理了不能直接写入 V8 快照的资源,可以实现下面两个钩子:
class AppBootHook { async snapshotWillSerialize() { // 在写入快照前关闭或解绑不可序列化资源 } async snapshotDidDeserialize() { // 在恢复后重新创建这些资源 } } module.exports = AppBootHook;snapshotWillSerialize()会在写入快照前执行。snapshotDidDeserialize()会在进程从快照启动后执行。
这两个钩子适合处理 timer、socket、process listener、logger 等需要在真实运行期重新建立的资源。
需要特别注意的是 agent 进程:在单进程快照模式下,agent.jsBoot 类的钩子也会运行——agent 的snapshotWillSerialize/snapshotDidDeserialize在 app 的之前触发(这一点在 packages/egg/src/lib/snapshot.ts 与 packages/egg/src/lib/snapshot.ts 中可以看到:无论构建还是恢复,都先app.agent.trigger...再app.trigger...)——因此agent.js持有的资源需要同样的处理,且恢复失败的错误也可能来自 agent 钩子。一个完整的实现示例:
class AppBootHook { constructor(app) { this.app = app; } async snapshotWillSerialize() { // 在写入 blob 前关闭/解绑不可序列化资源 clearInterval(this.timer); this.timer = null; } async snapshotDidDeserialize() { // 在恢复后的真实进程里重建 this.timer = setInterval(() => this.app.doWork(), 1000); } } module.exports = AppBootHook;性能
由于模块图已经加载、应用也已启动到configWillLoad,恢复阶段只需要付出didReady与连接/监听的成本。在 cnpmcore 上实测:
| 启动方式 | 恢复 → 监听 |
|---|---|
| 普通 bundle 启动 | ~942 ms |
| 快照恢复 | ~233 ms(快约 4 倍) |
模块图越大(插件、tegg 模块、Router 越多),收益越明显——这正是快照在构建期提前承担的开销。
已知限制
- 恢复需要 Node.js >= 24(见上文版本要求小节)。
- 仅单进程:快照以单个自包含进程运行(
mode: 'single'),与 bundle 一致,不支持 cluster 模式。 - 原生 addon 为 external,必须在部署目标上存在。
- 第三方依赖受限:任何在模块求值阶段就打开活跃资源或捕获不可序列化状态的依赖(打开的 socket、原生 HTTP/2 绑定、后台 timer、文件句柄)都必须要么保持 external(
--force-external),要么实现快照生命周期钩子,在序列化前释放、在恢复后重建。并不是每个包都开箱即可被快照化。 - Web 全局对象必须在调用处引用:undici 支撑的全局对象(
fetch/Headers/Request/Response/FormData/WebSocket/……)会在构建期被替换为桩(触碰它们会拉起 undici 不可序列化的原生绑定),并在恢复时重新安装,因此在调用处使用时可正常工作。但在模块求值期捕获的绑定——const f = fetch,或class X extends globalThis.Request——会把构建期的桩固化进 blob 且不会被升级。请在使用处引用 Web 全局对象,不要在模块顶层捕获。
支持范围仍在演进中;完整的已知限制与设计取舍记录在项目的 V8 快照 RFC 中。
故障排查
如果快照无法构建(序列化期间原生中止)或无法恢复,参见 快照故障排查。其中涵盖了构建期与恢复期的错误特征、如何定位捕获了不可序列化状态的模块,以及可用的修复手段(--force-external、egg.snapshot.lazyModules、生命周期钩子)。下面摘要其核心方法论。
唯一的规则:堆里不能有「活的」资源
快照在构建期冻结堆、在恢复期解冻。有三类值无法通过这个往返过程:
- 原生(C++ 支撑的)绑定——llhttp
HTTPParser、nghttp2、TLSSecureContext、DNSChannelWrap、原生 addon 等。 - libuv 句柄——打开的 socket、监听中的 server、timer、文件句柄、watcher、以及 IPC channel。
- Node 的惰性 web 全局 getter——
fetch、Headers、Request、Response、FormData、WebSocket、EventSource、MessageEvent、CloseEvent,以及Blob/File都是访问器属性,首次触碰时会初始化 Node 内建 undici(→ 原生 http/http2)。访问器本身不可序列化。
当一个包在模块求值阶段、或在configWillLoad(构建停止点)之前创建了上述任意一种资源,它就是快照不安全的。同一个包如果把这些工作推迟到只在请求期运行的函数里,通常就没问题。
构建期失败 vs 恢复期失败
快照可能在两个不同的点失败,错误特征会告诉你是哪一个:
- 构建期失败:当 V8 序列化器遇到不可序列化的值时,会以原生方式中止进程——没有可捕获的 JS 错误。典型特征如
node ... --build-snapshot worker.js was killed by signal SIGSEGV、exited with code 1,以及snapshot build finished but no blob was written at <output>/snapshot.blob。如果应用在加载元数据时抛出普通错误(配置错误、文件缺失),worker 会在退出前打印[egg-bundler] failed to build snapshot: <message>。 - 恢复期失败:
egg-scripts start --snapshot-blob <blob>恢复堆时,常见特征与含义对照如下:
| 现象 | 含义 |
|---|---|
反序列化中途原生 fatalCheck failed: current == end_slot_index | 在Node.js < 24上恢复。请始终在 Node.js >= 24 上恢复。 |
[egg-bundler] V8 snapshot restore requires Node.js >= 24, but this process is vX | 快照自带的守卫触发了(绕过了egg-scripts,或它的版本探测 fail-open)。 |
Error: Cannot find module '<pkg>' | 某个external依赖缺失。external 在恢复时会被require()真实加载,且解析根锚定在worker.js所在目录——请让worker.js与包含这些依赖的node_modules放在一起。 |
Aop Advice(X) not found in loadUnits | 某个 tegg 装饰类在 bundle 里保留了错误的源码路径。 |
某个「打包前还好好的」库内部深处抛TypeError | 该库错误地处理了构建期的成员代理桩。 |
globalThis.fetch(...)静默无反应 | 恢复后 web 全局对象仍是空操作桩。 |
[egg-bundler] failed to restore snapshot: <err> | 在完成延后生命周期(snapshotDidDeserialize→didReady→listen)时抛出的任何其他错误。 |
定位罪魁祸首模块
- 先检查构建环境:
egg-bin snapshot build在 spawn 子进程前会剥离它自己注入的 TypeScript loader,但会继承 shell 环境的其余部分。如果NODE_OPTIONS装入了自定义 loader 或 hook(--require ts-node/register、--loader …、--import …),它可能把不可序列化状态拉进堆。请先在干净环境里构建:
$ unset NODE_OPTIONS # 去掉任何继承来的 --require / --loader / --import $ egg-bin snapshot build- 打开调试日志:bundler 和启动器的每个阶段都通过
util.debuglog打日志:
# bundler 流水线(manifest → entry → pack → prelude) $ NODE_DEBUG='egg/bundler/*,egg/bin/commands/snapshot' egg-bin snapshot build # 启动器(恢复守卫、spawn) $ NODE_DEBUG='egg/scripts/commands/start' egg-scripts start --snapshot-blob ./dist-bundle/snapshot.blob常用命名空间:egg/bundler/bundler(打包开始、externals 解析结果、prelude/惰性 hook 注入计数)、egg/bundler/entry-generator(收集到的 bundle 入口、生成的 worker 入口路径)、egg/bundler/manifest-loader(发现并 externalize 了哪些内容)、egg/bundler/snapshot-prelude(哪些 external 的导出名无法读取)、egg/bin/commands/snapshot(实际 spawn 的node --build-snapshot …命令)。
- 直接读原生中止信息:构建以继承 stdio 的方式 spawn 子进程,因此 V8 序列化器的中止信息已打印到终端。可以从输出目录手动执行被包裹的命令以更快迭代:
$ cd ./dist-bundle $ EGG_BUNDLE_SNAPSHOT=build \ node --snapshot-blob ./snapshot.blob --build-snapshot ./worker.js若只想打印这条命令而不运行,用egg-bin snapshot build --dry-run。
- 用
--skip-bundle二分:--skip-bundle只对已有的worker.js重跑快照步骤,跳过缓慢的打包。由于 bundle 是单一自包含文件,可以在worker.js里注释掉某个import/require,几秒钟内重跑:
# 1. 打包一次 $ egg-bin snapshot build --output ./dist-bundle # 2. 编辑 ./dist-bundle/worker.js——注释掉某个嫌疑模块的求值 # 3. 只重跑快照构建 $ egg-bin snapshot build --output ./dist-bundle --skip-bundle如果去掉某个模块的求值后 blob 能构建成功,那这个模块就是元凶。
- 用
--force-external确认:把嫌疑包推出 bundle 既是诊断手段也是修复手段。external 永远不会在构建期被求值——它在恢复时被真实require()——所以如果--force-external <pkg>让构建成功了,说明该包在 import 时捕获了不可序列化状态:
$ egg-bin snapshot build --force-external some-native-client修复手段(按最轻量优先)
- 让包保持 external——最适合在 import 时打开连接、启动 timer 或加载原生 addon 的第三方包:
$ egg-bin snapshot build \ --force-external undici \ --force-external some-native-driver该包(及其自身依赖)必须安装在部署目标上,因为它在运行期加载,并没有被烤进 blob。反向标志--inline-external <pkg>会把解析器自动 externalize 的包强制塞回 bundle。
- 把 builtin 加入
egg.snapshot.lazyModules——对于在 import 时初始化原生状态、但不在默认惰性列表里的 builtin(或类 builtin 的 id):
{ "egg": { "snapshot": { "lazyModules": ["node:zlib", "node:perf_hooks"] } } }内建默认值已经覆盖了http、https、http2、tls、dns和inspector(含它们的node:形式)——这些无需自行列出。
实现快照生命周期钩子——当你自己的Boot 代码持有不可序列化的资源(timer、socket、logger 流、连接池)时,在序列化前释放、在恢复后重建(示例见上文「快照生命周期钩子」一节)。
把工作移出模块作用域——最干净的修复往往就在自己的代码里:把资源创建从顶层模块体移到一个在请求期运行的函数中(或放进
didReady/snapshotDidDeserialize)。一个在求值期只定义类和函数的模块永远是快照安全的;而一个在求值期就建立连接或启动 timer的模块则不是:
// ✗ 在模块求值期运行 → 被写入快照 const client = new SomeClient({ keepAlive: true }); // ✓ 首次使用时、在真实进程里创建 let client; function getClient() { return (client ??= new SomeClient({ keepAlive: true })); }- 避开 web 全局对象——
globalThis.fetch和其他 undici 支撑的全局对象在恢复后仍是空操作桩。请改用按需懒加载的 HTTP 客户端——把urllib或undici保持 external,在恢复时真实 require:
// ✗ 恢复后空操作 await fetch(url); // ✓ 真实客户端,恢复时真实加载 const { request } = require('urllib'); await request(url);两个易踩的失败模式
- tegg 装饰器「Aop Advice not found」:tegg 装饰器(
@SingletonProto、@HTTPController、@Advice等)会在模块求值时从调用栈用硬编码的栈深度捕获类的源码路径。在 bundle 里所有用户栈帧都坍缩到worker.js上,因此一个读取比常规更深栈帧的装饰器(典型是@Advice)会捕获到worker.js而非自己的文件。bundler 会自动纠正这一点:在序列化前根据 manifest 里 tegg 的decoratedFiles为每个装饰导出重新打上filePath。如果你写了自定义装饰器并撞上此错误,请确认该装饰文件属于某个 tegg module(这样它才会出现在decoratedFiles里)。 - 请求期文件缺失(运行期资源):一个能干净恢复的快照,仍可能在某个处理器读取文件时
ENOENT。只有app/下的非源码文件(加上强制拷贝目录app/public、app/assets、app/static)会被拷贝到worker.js旁边。源码扩展名文件、app/之外的资源、以及符号链接资源都不会被拷贝。由于 bundle 把__dirname和import.meta.url重写成了输出目录,执行fs.readFileSync(path.join(__dirname, 'tpl.html'))的模块会相对 bundle 输出目录解析。在module.yml里声明这些额外资源即可:
bundle: runtimeAssets: roots: ['app', 'resources'] forceCopyDirs: ['app/public', 'resources/templates']配置参考
| 机制 | 位置 | 用途 |
|---|---|---|
--force-external <pkg> | egg-bin snapshot build标志(可重复) | 把包留在 bundle 外,恢复时真实加载。 |
--inline-external <pkg> | egg-bin snapshot build标志(可重复) | 把被自动 externalize 的包强制塞回 bundle。 |
egg.snapshot.lazyModules | 应用package.json | 向惰性 external 集合添加 builtin/类 builtin 的 id(合并到默认值之上)。 |
snapshotWillSerialize()/snapshotDidDeserialize() | app.js/agent.jsBoot 类 | 释放并重建你自己代码持有的资源。 |
--pack-alias <spec>=<target> | egg-bin snapshot build标志(可重复) | 打包期重定向某个模块说明符。 |
--skip-bundle | egg-bin snapshot build标志 | 只对已有worker.js重跑快照步骤。 |
--dry-run | egg-bin snapshot build标志 | 打印node --build-snapshot命令但不 spawn。 |
bundle.runtimeAssets.roots/forceCopyDirs | 应用module.yml | 把额外的非源码文件拷进 bundle,使其在请求期存在。 |
--no-sourcemap | egg-scripts start标志 | 当自动注入的--import source-map-support/register干扰恢复启动时(TypeScript 应用)将其去掉。 |
NODE_OPTIONS | 环境变量 | 构建前必须不含自定义--loader/--require/--import(它们会随快照子进程一起进入)。 |
NODE_DEBUG=egg/bundler/* | 环境变量 | 追踪 bundler/启动器流水线。 |
相关源码路径
想深入理解实现细节的读者,可以在仓库中继续研读以下文件:
- 快照构建与恢复 API 实现:packages/egg/src/lib/snapshot.ts
egg-bin snapshot buildCLI 命令:tools/egg-bin/src/commands/snapshot.tsegg-scripts start --snapshot-blob恢复启动:tools/scripts/src/commands/start.ts- 打包期惰性 external 与 prelude 注入:tools/egg-bundler/src/lib/Bundler.ts
- Bundle 部署(快照的基础):site/docs/zh-CN/core/bundle.md
- 快照故障排查完整指南:site/docs/zh-CN/advanced/snapshot-troubleshooting.md
- 后端
- Web框架
【免费下载链接】egg
🥚🥚🥚🥚 Born to build better enterprise frameworks and apps with Node.js & Koa. https://307.run/eggcode
相关推荐
深入解析Agents-A1-8bit架构:MoE专家混合模型的技术奥秘
深入解析Agents A1 8bit架构:MoE专家混合模型的技术奥秘 Agents A1 8bit是基于MLX框架的8位量化视觉语言模型,采用创新的MoE(专
后端Web框架CubeSandbox 核心操作性能基准测试实战报告:从冷启动到快照的全链路压测指南
CubeSandbox 核心操作性能基准测试实战报告:从冷启动到快照的全链路压测指南 本报告基于 CubeSandbox 官方基准测试文档 docs/blog/
Agent 沙箱虚拟化云原生人工智能后端容器运行时FlutterUnit启动优化实战:从3秒到0.5秒的冷启动加速指南
FlutterUnit启动优化实战:从3秒到0.5秒的冷启动加速指南 你是否遇到过这样的情况:用户点击FlutterUnit图标后,屏幕长时间停留在启动界面,甚
前端移动开发桌面应用教育
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考