实战指南:单实例多应用的环境变量、生命周期管理与隔离原理)
NocoBase 共享内存多应用模式local实战指南单实例多应用的环境变量、生命周期管理与隔离原理【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase共享内存多应用模式是 NocoBase 多应用管理Multi App能力中的轻量级方案在一个 NocoBase 实例、一个进程内同时运行多个独立应用每个应用可连接独立数据库、拥有独立 JWT 密钥与自定义域名但用户只需维护一个实例。本文将基于官方文档 共享内存模式 为主体结合仓库源码app-supervisor、plugin-multi-app-manager 等深入讲解环境变量配置、应用创建/启动/停止/删除的完整操作、数据库创建与适配器体系原理读完你可以独立完成业务应用级拆分并理解其隔离边界。一、什么是共享内存多应用模式当用户希望对业务进行应用级别的拆分例如将 CRM、售后、运营后台、数据分析拆分为独立应用但又不希望引入复杂的部署和运维架构时可以使用共享内存的多应用模式。在这种模式下一个 NocoBase 实例中可以同时运行多个应用。每个应用是独立的可以连接独立的数据库或独立的 Schema可以单独创建、启动和停止共享同一个进程和内存空间用户仍然只需要维护一个 NocoBase 实例。从资源角度看相比多进程或多容器方案共享内存模式资源占用更低但也正因为所有应用运行在同一进程中它们会共享 CPU、内存等资源单个应用的异常或高负载可能影响其他应用的稳定性因此它更适合业务拆分初期、应用数量可控的阶段。当应用规模持续增长、对隔离性和稳定性提出更高要求时可以进一步演进到「多环境混合部署」架构参见 多应用管理总览。从代码层面看这套能力由AppSupervisor应用监管器统一承载它是整个多应用体系的核心控制器负责应用的发现Discovery、进程管理Process与命令下发Command下文会详细展开。二、开启共享内存模式环境变量配置在使用多应用功能前请确保在 NocoBase启动时设置以下环境变量APP_DISCOVERY_ADAPTERlocal APP_PROCESS_ADAPTERlocalAPP_DISCOVERY_ADAPTER应用发现适配器名称负责应用模型的读取、状态维护与 Web 流量分发APP_PROCESS_ADAPTER应用进程适配器名称负责应用实例的创建、启动、停止、删除与错误管理。在共享内存模式下两者都取值为local表示应用模型存储在本机、应用也运行在本进程内。环境变量的底层解析机制AppSupervisor在构造时会解析这两个环境变量源码位于 packages/core/server/src/app-supervisor/index.tsprivate resolveDiscoveryAdapterName() { return process.env.APP_DISCOVERY_ADAPTER || AppSupervisor.defaultDiscoveryAdapterName; } private resolveProcessAdapterName() { return process.env.APP_PROCESS_ADAPTER || AppSupervisor.defaultProcessAdapterName; }即未设置时回退到默认适配器名仓库中默认注册了main-only适配器多应用管理插件会额外注册legacy适配器并设为默认见 plugin-multi-app-manager/src/server/server.ts。适配器采用工厂注册表机制扩展AppSupervisor.registerDiscoveryAdapter(main-only, ({ supervisor }) new MainOnlyAdapter(supervisor)); AppSupervisor.registerProcessAdapter(main-only, ({ supervisor }) new MainOnlyAdapter(supervisor));如果解析出的适配器工厂不存在createDiscoveryAdapter/createProcessAdapter会尝试回退到默认适配器仍找不到时抛出No AppDiscovery adapter available/No AppProcess adapter available错误。因此必须确认所用 NocoBase 版本支持local适配器名称并保证两个环境变量配对设置否则实例可能无法按预期进入共享内存模式。此外AppSupervisor 还支持APP_COMMAND_ADAPTER命令适配器以及可选的APP_SUPERVISOR_AES_SECRET_KEY用于解密子应用配置中加密的数据库密码可在 index.ts 中查看相关初始化逻辑。三、创建子应用配置项逐项解析在系统设置菜单中点击「应用监管器」进入应用管理页面点击「新增」按钮创建一个新应用。注意应用管理页面由nocobase/plugin-multi-app-manager插件提供且该插件只能在主应用main中启用见 server.ts。创建表单的配置项说明如下配置项说明应用名称应用在界面中显示的名称对应displayName字段应用标识应用标识全局唯一对应name字段自动生成a_前缀的 UID启动方式- 首次访问时启动autoStart: false当用户首次通过 URL 访问该子应用时才启动- 随主应用一同启动autoStart: true在主应用启动时同时启动子应用会增加主应用启动时间环境在共享内存模式下只有本地环境可用即local数据库连接用于配置应用的主数据源支持三种方式- 新数据库new_database复用当前数据库服务创建独立数据库- 新的数据连接new_connection连接到其他数据库服务- Schema 模式new_schema当前主数据源为 PostgreSQL/Kingbase 时为应用创建独立的 Schema升级若连接的数据库中存在低版本的 NocoBase 应用数据时是否允许自动升级到当前应用版本JWT 密钥为应用自动生成独立的 JWT 密钥确保应用会话独立于主应用及其他应用对应options.authManager.jwt.secret自定义域名为应用配置独立访问域名对应cname字段全局唯一配置项对应的数据模型应用信息存储在applications数据表中其集合定义见 collections/applications.tsexport default defineCollection({ name: applications, filterTargetKey: name, fields: [ { type: uid, name: name, primaryKey: true }, // 应用标识主键 { type: string, name: displayName }, // 应用名称 { type: string, name: cname, unique: true }, // 自定义域名唯一 { type: boolean, name: pinned }, // 是否固定到菜单 { type: string, name: icon }, { type: string, name: status, defaultValue: pending }, // 应用状态 { type: json, name: options }, // 应用配置启动方式、JWT 密钥、数据库连接等 ], });前端创建表单的定义client/settings/schemas/applications.tsx与上述字段一一对应displayName、nameuid校验创建时自动生成a_${uid()}、options.autoStart两种启动方式单选、cname自定义域名、pinned、options.authManager.jwt.secret独立 JWT 密钥带「独立 JWT 密钥确保数据与会话与其他应用隔离」的说明文案。创建时的底层流程当应用创建成功后服务端会触发applications.afterCreateWithAssociations事件见 server.ts核心逻辑为校验名称main为保留名称不可作为子应用标识对应测试用例 multiple-apps.test.ts通过supervisor.registerApp({ appModel, mainApp })注册应用实例调用supervisor.createDatabase({ app, appOptions })按dbConnType创建数据库/Schema若请求上下文中带waitSubAppInstall则同步执行subApp.runCommand(start, --quickstart)完成安装与启动否则异步执行。仓库测试 multiple-apps.test.ts 覆盖了「创建应用后状态为 running」「合并数据库选项」「按需注册数据库创建器」「创建带自定义插件的应用」等关键路径可作为理解该流程的验证参考。四、启动子应用两种启动方式创建完成后点击「启动」按钮可启动子应用如果创建时勾选了**“首次访问时启动”**则首次访问时会自动启动。两种启动方式的行为差异首次访问时启动options.autoStart: false应用模型被创建但不随主应用启动首次请求命中该应用时才触发启动。主应用启动更快适合低频或按需使用的应用随主应用一同启动options.autoStart: true主应用afterStart时会自动注册并启动所有autoStart: true的子应用见 server.ts。代价是增加主应用的启动时间。「停止」操作通过applications:stop资源动作转发给supervisor.stopApp「启动」对应applications:start→supervisor.startApp两者在 server.ts 中注册。值得注意的是AppSupervisor 为每个应用维护了互斥锁并通过可配置并发的引导队列bootstrapQueue来串行化启动过程避免同一应用被并发启动相关实现可参考 legacy-adapter.ts其中SUBAPP_BOOTSTRAP_CONCURRENCY、SUBAPP_BOOTSTRAP_INTERVAL_CAP、SUBAPP_BOOTSTRAP_INTERVAL三个环境变量可控制子应用启动的并发度与间隔。测试用例「should get same obj ref when asynchronously access with same sub app name」也验证了并发访问同一子应用时只会创建一个实例。五、访问子应用路径访问与自定义域名点击「访问」按钮会在新标签页中打开该子应用。默认使用/apps/:appName/admin/路径访问子应用例如http://localhost:13000/apps/a_7zkxoarusnx/admin/其中a_7zkxoarusnx即创建时自动生成的应用标识name。主应用网关Gateway会根据 URL 中的应用标识把请求路由到对应子应用实例见 server.ts 中的addAppSelectorMiddleware机制。同时也可以为子应用配置独立的域名在创建/编辑表单的「自定义域名」中填写域名对应cname字段全局唯一将域名DNS 解析到当前服务器 IP如果使用了 nginx也需要在 nginx 配置里添加该域名的反向代理配置。底层路由逻辑网关中间件会读取请求的 Hostx-hostname头或 hostname在applications表中按cname精确匹配应用命中后把请求解析到对应子应用server.ts。客户端侧的访问地址生成与自定义域名判定逻辑可参考 plugin-client/src/server/appPortals.ts。建议为不同应用配置独立域名同一域名子路径下不同应用切换时可能涉及会话重新登录问题详见下文「应用会话」。六、停止与删除应用停止点击「停止」按钮可停止子应用有二次确认。停止后应用实例从进程中被移除状态更新为stopped再次访问时会按启动方式重新拉起删除点击「删除」按钮可移除应用。服务端监听applications.afterDestroy事件并调用supervisor.removeApp将应用实例从监管器中移除server.ts。仓库测试 multiple-apps.test.ts 验证了「删除应用记录后AppSupervisor 中不再持有该应用实例」。七、应用状态解析在应用监管器列表中可查看每个应用的当前状态。结合 client/settings/schemas/applications.tsx 与 AppSupervisor 的状态机完整状态枚举如下状态含义preparing准备中应用即将进入引导启动流程initializing初始化中正在创建/初始化应用实例initialized已初始化实例已创建但尚未启动完成running运行中应用正常对外服务commanding执行命令中正在执行升级、插件安装等维护命令stopped已停止实例已停止error错误启动或命令执行出现致命错误not_found未找到引导后未找到对应应用实例状态由discoveryAdapter维护并在appStatus中缓存。应用事件__started/__stopped/maintaining会驱动状态流转例如启动完成后置为running停止后置为stopped维护命令期间进入commanding出现fatal级别错误时置为error见 index.ts。应用列表接口会实时附加每个应用的状态applications:list后置处理器。八、底层原理AppSupervisor 与数据库创建器8.1 适配器体系Discovery / Process / CommandAppSupervisor是单例getInstance()内部由三类适配器协作完成多应用管理DiscoveryAdapter发现适配器负责应用模型AppModel的增删改查、应用状态appStatus、环境注册与心跳、以及 Web/WebSocket 流量代理proxyWeb/proxyWsProcessAdapter进程适配器负责应用实例的创建、启动、停止、删除createApp/startApp/stopApp/removeApp/upgradeApp以及应用错误记录CommandAdapter命令适配器负责跨环境维护命令的分发。local模式下发现与进程适配器同名AppSupervisor 会优先复用发现适配器作为进程适配器当它实现了addApp/startApp方法时见 index.ts这正是「共享内存、单进程运行多个应用」的实现基础。8.2 数据库创建器AppDbCreator应用创建时如何落库取决于数据库连接配置项。AppSupervisor 使用条件注册表ConditionalRegistry把三种创建策略与dbConnType条件绑定db-creator.tsexport const createDatabaseCondition ({ appOptions }) !appOptions?.dbConnType || appOptions.dbConnType new_database; // 新数据库 export const createConnectionCondition ({ appOptions }) appOptions?.dbConnType new_connection; // 新的数据连接 export const createSchemaCondition ({ appOptions }) appOptions.dbConnType new_schema; // Schema 模式三种策略的实际行为dbConnType适用场景底层实现new_database默认复用当前数据库服务创建独立数据库MySQL/MariaDBCREATE DATABASE IF NOT EXISTSPostgreSQL/Kingbase检查pg_database后CREATE DATABASEnew_connection连接到其他数据库服务对目标服务执行建库PostgreSQL 下若指定了schema则CREATE SCHEMA IF NOT EXISTSnew_schema主数据源为 PostgreSQL/Kingbase 时创建独立 SchemaCREATE SCHEMA IF NOT EXISTS仅支持 postgres/kingbase其他方言直接抛错Schema is only supported for postgres/kingbase8.3 应用选项工厂AppOptionsFactory子应用的默认配置由appOptionsFactory基于主应用生成app-options-factory.ts关键逻辑默认dbConnType new_database数据库名取应用标识rawDatabaseOptions.database appName主数据源为SQLite时为每个应用生成独立的.sqlite文件与主应用存储同目录命名为${appName}.sqlite当USE_DB_SCHEMA_IN_SUBAPP true且方言为 PostgreSQL/Kingbase 时自动切换为new_schema模式以应用标识作为 Schema 名每个子应用的缓存cacheManager使用appName作为前缀避免多应用缓存键冲突子应用默认加载[nocobase]插件集并沿用主应用的日志配置。子应用实际的配置为「默认选项」与「创建时填写的选项」深度合并的结果测试用例「should merge database options」验证了这一点。九、常见问题与最佳实践1. 插件管理其他应用可以使用的插件与主应用一致包括版本但每个应用可以独立配置和启用/停用插件。子应用创建时也可通过options.plugins指定启用的插件及插件配置见测试「should create with plugins」。2. 数据库隔离其他应用可以配置独立的数据库或独立 Schema实现数据层面的物理隔离。如果希望应用之间共享数据可通过外部数据源功能实现而不应依赖共享同一数据库。3. 数据备份和迁移目前主应用上的数据备份不包含其他应用的数据只包含应用的基本信息需要在其他应用内分别手动备份和迁移各自的数据。规划多应用方案时请提前为每个子应用建立独立的备份策略。4. 部署与更新在共享内存模式下其他应用的版本会自动跟随主应用升级自动保证应用版本一致。主应用执行upgrade时subAppUpgradeHandler会遍历应用记录为每个子应用执行升级server.ts。仓库测试分别覆盖了「主应用升级时自动升级 autoStart 子应用」与「未设置 autoStart 的子应用不随主应用升级」两种行为。因此升级主应用前应确认所有子应用均处于可升级状态。5. 应用会话安全边界若应用使用独立的 JWT 密钥应用会话独立于主应用及其他应用。但通过同一域名的子路径访问不同应用时由于应用 TOKEN 缓存在 LocalStorage 中在不同应用间切换时需要重新登录。建议为不同应用配置独立域名以实现更好的会话隔离若应用未使用独立的 JWT 密钥会共享主应用的会话同一浏览器访问其他应用后返回主应用无需重新登录体验更顺滑。但存在安全隐患如果不同应用的用户 ID 重复可能导致用户越权访问其他应用的数据。出于安全考虑多租户/多客户场景下强烈建议为每个应用开启独立 JWT 密钥并配合独立域名使用。十、小结共享内存多应用模式APP_DISCOVERY_ADAPTERlocalAPP_PROCESS_ADAPTERlocal是 NocoBase 在「单应用」与「多环境混合部署」之间的理想折中通过AppSupervisor的适配器体系与数据库创建器在一个进程内实现多个物理隔离应用的创建、启动、停止与访问适合业务模块拆分、团队并行开发、演示与测试环境等场景。需要牢记其两个边界资源上共享 CPU/内存、备份与升级以主应用为中心同时通过独立 JWT 密钥与独立域名守住会话隔离的安全底线。当应用规模进一步增长时可参考 多应用管理总览 演进到多环境混合部署架构。【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考