Encore Backend Framework 代码片段速查手册:API、数据库、Cron、PubSub、缓存与密钥的实战用法
2026/9/15 17:31:27 网站建设 项目流程

Encore Backend Framework 代码片段速查手册:API、数据库、Cron、PubSub、缓存与密钥的实战用法

【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encore

导读

本文是 Encore 开源仓库(encor/encore)中面向 Go 后端的速查型技术指南,围绕 docs/go/primitives/code-snippets.md 展开:当你已经熟悉 Encore 的模型后,可以跳过冗长的概念讲解,直接复制粘贴这些经过验证的代码片段来搭建 API、SQL 数据库、定时任务、PubSub 事件流、Redis 缓存集群与密钥管理。文章在完整继承原文档全部示例的基础上,结合runtimes/go下的真实运行时源码(sqldbcronpubsubcachesecrets)与cli/cmd/encore/secrets的 CLI 实现,补充了参数取值范围、默认值与底层调用链,让你既"抄得走"也"读得懂"。

适用前提:以下所有代码片段均以当前仓库runtimes/go的 Go 运行时 API 为准,需要你的应用基于 Encore 的 Go Backend Framework 构建,并通过encore run启动本地开发环境。

APIs:定义、调用与裸 HTTP 端点

定义 API(//encore:api注解)

在 Encore 中,服务就是一个普通的 Go 包。把任意一个函数加上//encore:api注解,它就会变成一个由 Encore 统一路由、鉴权、追踪和部署的 API 端点:

package hello // service name //encore:api public func Ping(ctx context.Context, params *PingParams) (*PingResponse, error) { msg := fmt.Sprintf("Hello, %s!", params.Name) return &PingResponse{Message: msg}, nil }

关键点:

  • 包名即服务名package hello意味着这个包是一个名为hello的 Encore 服务;
  • public表示公开端点(另有private,只能被其他服务或平台内部调用,外部请求不可直达);
  • 端点的签名约定为func(ctx context.Context, params *T) (*R, error)params与返回值都可以是结构体指针或简单类型,返回值中的error会被 Encore 自动映射为 HTTP 状态码与错误响应;
  • 请求/响应 schema 会自动生成 OpenAPI 文档与类型安全的客户端(见 pkg/clientgen)。

定义请求与响应 Schema

与端点配套的结构体即请求/响应 schema,字段名默认采用 Go 风格自动映射为 JSON 字段:

// PingParams is the request data for the Ping endpoint. type PingParams struct { Name string } // PingResponse is the response data for the Ping endpoint. type PingResponse struct { Message string }

调用 API:类型安全的函数调用

跨服务调用在 Encore 里就是一次普通的 Go 函数调用——编译器会为你生成 RPC 封装,无需手动构造 HTTP 请求:

import "encore.app/hello" // import service //encore:api public func MyOtherAPI(ctx context.Context) error { resp, err := hello.Ping(ctx, &hello.PingParams{Name: "World"}) if err == nil { log.Println(resp.Message) // "Hello, World!" } return err }

提示:导入服务包,然后像调用普通函数一样调用 API 端点即可。请求会通过 Encore 的服务网格自动完成路由、鉴权、追踪与分布式请求传播。

接收 Webhook(raw 端点)

当需要完全控制 HTTP 请求(例如接收第三方 Webhook、做自定义路由或接入现有net/http中间件)时,使用raw端点:

import "net/http" // Webhook receives incoming webhooks from Some Service That Sends Webhooks. //encore:api public raw func Webhook(w http.ResponseWriter, req *http.Request) { // ... operate on the raw HTTP request ... }

提示:与普通端点一样,它也会被公开暴露,URL 形如:

https://<env>-<app-id>.encr.app/service.Webhook

raw 端点直接给你http.ResponseWriter*http.Request,因此可以读取原始 body、请求头,写入任意响应内容,适合做 Stripe/GitHub 这类第三方 Webhook 接收器。

Databases:创建、写入与查询 SQL 数据库

创建 SQL 数据库并定义迁移

导入encore.dev/storage/sqldb,调用sqldb.NewDatabase并把结果赋给包级变量即可声明一个数据库;sqldb.DatabaseConfig.Migrations指定存放迁移 SQL 文件的目录,它就是数据库 schema 的定义方式:

-- todo/db.go -- package todo // Create the todo database and assign it to the "tododb" variable var tododb = sqldb.NewDatabase("todo", sqldb.DatabaseConfig{ Migrations: "./migrations", }) // Then, query the database using db.QueryRow, db.Exec, etc. -- todo/migrations/1_create_table.up.sql -- CREATE TABLE todo_item ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, done BOOLEAN NOT NULL DEFAULT false -- etc... );

补充说明(基于 runtimes/go/storage/sqldb/sqldb.go 与 db.go 的源码):

  • 底层连接池由pgxpool.Pool管理(Database结构体中的pool *pgxpool.Pool),并通过database/sql兼容层暴露Stdlib()方法,可无缝接入期望*sql.DB的第三方库;
  • 迁移采用版本化 SQL 文件(如1_create_table.up.sql),Encore 在本地开发环境自动执行迁移,让数据库与代码保持一致;
  • 每个Database都会自动注入分布式追踪(DBQueryStart/DBQueryEnd等 trace 事件),你在本地开发仪表盘上可以直接看到每条 SQL 的执行情况。

插入数据

使用包方法sqldb.Exec(底层即Database.Exec)执行带占位符参数的 SQL,避免 SQL 注入:

import "encore.dev/storage/sqldb" // insert inserts a todo item into the database. func insert(ctx context.Context, id, title string, done bool) error { _, err := tododb.Exec(ctx, ` INSERT INTO todo_item (id, title, done) VALUES ($1, $2, $3) `, id, title, done) return err }
  • 参数占位符使用 PostgreSQL 风格$1, $2, $3
  • 返回的ExecResult可通过RowsAffected()获取受影响行数(sqldb.go)。

查询单行数据

使用sqldb.QueryRow查询一行,配合Scan将列映射到 Go 变量:

import "encore.dev/storage/sqldb" var item struct { ID int64 Title string Done bool } err := tododb.QueryRow(ctx, ` SELECT id, title, done FROM todo_item LIMIT 1 `).Scan(&item.ID, &item.Title, &item.Done)

提示:如果QueryRow没有找到匹配行,会返回一个错误,可通过导入标准库errors包调用errors.Is(err, sqldb.ErrNoRows)判断。ErrNoRows在源码中定义为sql.ErrNoRows(见 sqldb.go),因此语义与database/sql完全一致。另外,数据库服务端报错会被转换为携带CodeSeverityTableName等结构化信息的*sqldb.Error,可用sqldb.ErrCode(err)进一步分类处理(见 errors.go)。

Defining a Cron Job:定义定时任务

使用encore.dev/croncron.NewJob定义一个定时任务,并把它赋值给包级变量(var _ =表示仅为了注册,无需持有引用):

import "encore.dev/cron" var _ = cron.NewJob("welcome-email", cron.JobConfig{ Title: "Send welcome emails", Every: 2 * cron.Hour, Endpoint: SendWelcomeEmail, }) //encore:api private func SendWelcomeEmail(ctx context.Context) error { // ... return nil }

提示:Cron Jobs 不会在本地开发环境中自动执行,你需要直接调用目标端点来测试实现。

从 runtimes/go/cron/cron.go 可以补充以下重要约束:

  • id必须是 kebab-case,不超过 63 个字符,以字母开头、以字母或数字结尾;这个 ID 是任务在平台侧的稳定标识,后续重构代码、移动定义位置时,Encore 靠它判断"还是同一个任务";
  • JobConfigEverySchedule二选一Every接受cron.Minute/cron.Hour常量构成的间隔,且间隔必须能整除 24 小时(如10 * cron.Minute6 * cron.Hour合法,7 * cron.Hour不合法,编译器会在编译期报错);Schedule则接受标准 Cron 表达式;
  • Endpoint的签名必须是func(context.Context) errorfunc(context.Context) (T, error),且不能带其他参数;
  • JobConfig的字段在编译期被解析并用于平台侧资源供给,因此必须写成常量字面量,运行时并不真正执行这段"配置"。

PubSub:主题、发布与订阅

创建 PubSub Topic

pubsub.NewTopic创建泛型主题(Topic[T]T是消息载荷类型),主题必须声明为包级变量,不能在函数内部创建:

import "encore.dev/pubsub" type SignupEvent struct { UserID int } var Signups = pubsub.NewTopic*SignupEvent

提示:无论主题定义在哪个服务,任何服务都可以向其发布消息或订阅。

配置细节(见 runtimes/go/pubsub/internal/types/public.go):

  • DeliveryGuarantee是必填字段,可选pubsub.AtLeastOnce(至少一次投递,AWS/GCP 无吞吐限制)或pubsub.ExactlyOnce(尽力恰好一次投递;需注意 ExactlyOnce 只约束"投递"而非"发布",若应用逻辑重试导致同一消息发布两次,仍会投递两次,所以订阅端处理函数最好保持幂等);
  • 可选OrderingAttribute指定消息属性作为排序键,保证相同取值消息按发布顺序投递;消息属性通过pubsub-attrstruct tag 声明(UserID string \pubsub-attr:"user-id"``);本地开发环境中排序键暂时不生效。

发布事件(Pub)

调用主题的Publish方法发布消息,返回唯一的消息 ID;Publish会阻塞直到消息被主题成功接收:

if _, err := Signups.Publish(ctx, &SignupEvent{UserID: id}); err != nil { return err } if err := tx.Commit(); err != nil { return err }

从 topic.go 可以看到Publish的内部链路:先校验ctx,将消息属性与 JSON body 序列化,自动注入encore_parent_trace_id/encore_ext_correlation_id等关联追踪属性,再经由限流器(publishLimiter.Wait)后调用底层云 Provider 的PublishMessage,同时记录PubsubPublishStart/End追踪事件——这意味着发布是"可观测的",并且会把 trace 上下文自动传递给订阅方。

提示:如果要从其他服务发布,导入该主题的包变量(本例中的Signups)并调用其Publish即可。

订阅事件(Sub)

通过pubsub.NewSubscription以包级变量创建订阅,指定要订阅的主题、订阅名与处理函数:

var _ = pubsub.NewSubscription( user.Signups, "send-welcome-email", pubsub.SubscriptionConfig[*SignupEvent] { Handler: SendWelcomeEmail, }, ) func SendWelcomeEmail(ctx context.Context, event *SignupEvent) error { ... send email ... return nil }

SubscriptionConfig中的Handler是必填字段:返回nil表示消息被确认(ack),不再重投;返回非nil错误会触发负确认(nack)与重试,直到达到重试上限。常用可选配置(见 types.go):

  • MaxConcurrency:单个服务实例并发处理的消息数上限(负值表示不限制;注意该配置对 Encore Cloud 不生效,部分云 Provider 会自适应并发);
  • AckDeadline:处理超时时间,默认 30 秒、至少 1 秒,超时后消息会被放回订阅;
  • MessageRetention:未投递消息的保留时长,默认 7 天;
  • RetryPolicyMinBackoff(默认 10 秒)/MaxBackoff(默认 10 分钟)/MaxRetries0时使用默认 100 次重试;pubsub.NoRetries立即进入死信队列;pubsub.InfiniteRetries永不进入死信)。

Defining a Cache cluster:定义 Redis 缓存集群

使用encore.dev/storage/cachecache.NewCluster声明一个 Redis 缓存集群,同样必须赋给包级变量:

import "encore.dev/storage/cache" var MyCacheCluster = cache.NewCluster("my-cache-cluster", cache.ClusterConfig{ // EvictionPolicy tells Redis how to evict keys when the cache reaches // its memory limit. For typical cache use cases, cache.AllKeysLRU is a good default. EvictionPolicy: cache.AllKeysLRU, })

从 runtimes/go/storage/cache/cache.go 可以看到 Encore 支持的全部淘汰策略(EvictionPolicy字符串常量):

常量Redis 策略行为
cache.AllKeysLRUallkeys-lru淘汰最久未使用的键,默认值,多数缓存场景的推荐选择
cache.AllKeysLFUallkeys-lfu淘汰最不常使用的键
cache.AllKeysRandomallkeys-random随机淘汰任意键
cache.VolatileLRUvolatile-lru仅淘汰设置了过期时间的键中最久未使用的
cache.VolatileLFUvolatile-lfu仅淘汰设置了过期时间的键中最不常使用的
cache.VolatileTTLvolatile-ttl淘汰剩余 TTL 最短的键
cache.VolatileRandomvolatile-random在设置了过期时间的键中随机淘汰
cache.NoEvictionnoeviction不淘汰任何键,内存达到上限时直接返回错误

底层客户端基于github.com/go-redis/redis/v8,并围绕"keyspace + KeyPattern + 过期策略"提供类型安全的读写 API(含MissKeyExists等可errors.Is判定的哨兵错误);对带默认过期时间的 keyspace,写操作会通过 Redis 事务管道(TxPipeline)把命令与过期时间更新合并为原子操作(见 cache.go)。

Secrets:定义、设置与使用密钥

定义 Secrets

在任意服务包中声明一个未导出的、名为secrets的结构体变量,字段为string类型,字段名即密钥名:

var secrets struct { GitHubAPIToken string // personal access token for deployments SomeOtherSecret string // some other secret }

提示:变量必须是未导出的、名为secrets的结构体,且所有字段类型必须是string。该结构体由 Encore 编译器特殊识别,不会被打包进镜像或提交到仓库。

设置 Secret 值

通过 Encore CLI 为密钥设置不同环境的值:

$ encore secret set --type <types...> <secret-name>

提示<types>决定该密钥值作用于哪些环境类型,用逗号分隔的组合包括productiondevelopmentpreviewlocal;每个密钥在每个环境类型下只能有一个值。

CLI 实现(cli/cmd/encore/secrets/set.go)提供了更完整的用法:

  • 环境类型支持别名:dev(=development)、prod(=production)、pr/ephemeral(=preview)、local,也可用--dev(等价于 development+preview+local)或--prod快捷标志;还可以用--env <environment-name>只针对某个具体环境设置;
  • 交互式输入:直接在终端运行命令后,CLI 会以隐藏输入提示Enter secret value:
  • 管道输入:$ encore secret set --type dev,local,pr MySecret < my-secret.txt,注意会去除值末尾的换行符;
  • 已存在的密钥/选择器组合会被更新而非重复创建;若选择器与已有密钥值冲突,CLI 会列出冲突明细。

使用 Secrets

在代码中直接通过secrets.<FieldName>读取,运行时由 Encore 从平台注入实际值:

func callGitHub(ctx context.Context) { req, _ := http.NewRequestWithContext(ctx, "GET", "https:///api.github.com/user", nil) req.Header.Add("Authorization", "token " + secrets.GitHubAPIToken) resp, err := http.DefaultClient.Do(req) // ... handle err and resp }

提示:密钥名在整个应用中全局唯一;如果多个服务使用相同的密钥名,运行时它们会收到同一个密钥值。密钥不会出现在构建产物或 Git 历史中,CLI 在设置完成后还会触发守护进程的SecretsRefresh以便本地环境立即生效(见 set.go)。

小结与下一步

这份速查手册覆盖了 Encore Go Backend Framework 的全部核心构建块:API(含 raw Webhook 端点)、SQL 数据库(sqldb的建库/迁移/增查)、定时任务(cron.NewJob)、事件驱动(pubsub的 Topic/Subscription)、Redis 缓存集群(cache.NewCluster)与密钥管理(secrets结构体 +encore secret set)。所有片段都可在新项目中直接复制使用,并享受 Encore 自动提供的分布式追踪、类型安全调用与云端资源供给。

需要深入了解某个主题时,可继续查阅仓库内对应文档:定义 API、数据库、Cron Jobs、PubSub、缓存、Secrets,以及对应运行时实现 runtimes/go/storage 与 runtimes/go/pubsub。

【免费下载链接】encoreThe infrastructure platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/encor/encore

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询