PAYATHON 2026

如何让 Encore.ts 各服务的初始化代码只在所属容器中执行?

数据小陈

调用初始化函数前,可以检查 appMeta().deploy.hostedServices 是否包含相应服务。该字段按服务名称记录当前部署单元托管的服务,可用于限制初始化代码的执行范围。使用前,先确认项目所用的 SDK 提供该字段,具体定义见 Encore SDK 源码。

为什么所有初始化函数都会执行?

示例中的 initServiceA() 和 initServiceB() 是在模块顶层直接调用的普通函数。只要对应模块被加载并求值,调用就会执行;函数名称和所在目录不会自动限制它们的服务范围。

题目描述的部署中,多个服务模块都会被加载,因此每个容器都可能输出两条初始化日志。需要为这些带副作用的调用加上明确的执行条件。

根据托管服务信息限制初始化

分别修改两个文件:

// serviceA/index.ts
import { appMeta } from "encore.dev";

function initServiceA() {
  console.log("Service A initialized");
}

if (appMeta().deploy.hostedServices["serviceA"]) {
  initServiceA();
}
// serviceB/index.ts
import { appMeta } from "encore.dev";

function initServiceB() {
  console.log("Service B initialized");
}

if (appMeta().deploy.hostedServices["serviceB"]) {
  initServiceB();
}

这里的 serviceA、serviceB 必须与 Encore 中声明的服务名称一致,不能仅凭目录名推断。

如果一个容器只托管 serviceA,判断条件就只会允许 initServiceA() 执行。如果同一个运行单元托管两个服务,两个初始化函数都执行也符合部署配置。

如何确认判断条件生效?

可以临时记录运行时返回的服务列表:

import { appMeta } from "encore.dev";

console.log(
  "Hosted services:",
  Object.keys(appMeta().deploy.hostedServices)
);

部署后,分别检查各容器的服务列表和初始化日志。如果 serviceA 容器仍列出了 serviceB,需要进一步检查服务分组和部署配置;如果列表只有 serviceA,却仍出现 B 的初始化日志,就应查找是否还有其他未加条件的初始化入口。

不要用 currentRequest() 判断启动阶段所属的服务。初始化时通常没有请求上下文,此时它会返回 undefined,这一行为在 Encore Metadata 文档中有说明。

初始化逻辑还要注意什么?

  • 将建立连接、启动后台任务等有副作用的操作放在条件内部。如果导入的模块已经在顶层执行这些操作,只包住后面的日志或函数调用无法阻止它们。
  • 保持 Encore 要求的服务、API 和基础设施声明结构,不要把这些声明一起移入条件分支。
  • 这一判断只限制执行位置。重启、扩容或多个运行进程仍可能分别执行初始化,涉及共享数据写入时需要考虑幂等性。
  • 如果实际初始化函数是异步的,还需要处理失败,并确保依赖初始化结果的请求等待其完成。示例沿用原问题中的同步函数,不提供异步启动就绪保证。
  • 如果项目类型定义中没有 deploy.hostedServices,应先核对 SDK 和运行时的兼容性,不要通过类型断言假定该字段存在。

备注:内容仅供参考。