Go基础云函数开发示例
目标:创建一个名为 hello
的 API 接口,它能够:
- 被通过
.../.netlify/functions/hello
访问。 - 可以接收一个可选的
name
查询参数,例如?name=Go
。 - 返回一个 JSON 格式的问候语。
第一阶段:创建一个最简单的 “Hello, World” 风格的 Go 云函数并部署到 Netlify。熟悉从本地开发、测试到线上部署的完整工作流。
开发环境准备
在开始之前,请确保您的电脑上已经安装了以下工具:
- Go 语言环境:这里使用的版本是 v0.147.3
- Netlify 账号:Signup
- Netlify CLI:用于本地测试。安装命令:
pnpm install netlify-cli -g
。 - Git 和 GitHub 账号:用于代码版本控制和部署。
初始化项目
首先,我们在本地创建一个项目目录并初始化 Go 模块。
-
打开您的终端或命令行工具。
-
创建项目目录并进入:
bashmkdir my-netlify-go-app cd my-netlify-go-app
-
初始化 Go 模块。这会创建一个
go.mod
文件,用于管理依赖。bashgo mod init my-netlify-go-app
-
创建 Netlify Functions 所需的目录结构:
bashmkdir -p netlify/functions/hello
netlify/functions/
是 Netlify 默认存放所有云函数的目录。hello/
是我们第一个函数的名字。
项目结构现在是:
/my-netlify-go-app
|-- go.mod
|-- netlify/
|-- functions/
|-- hello/
编写Go云函数代码
现在,我们来编写函数的具体逻辑。
-
在
netlify/functions/hello/
目录下创建一个main.go
文件。 -
将以下代码复制并粘贴到
main.go
文件中:go// netlify/functions/hello/main.go package main import ( "encoding/json" "fmt" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) // Response is a custom struct for our JSON response type Response struct { Message string `json:"message"` } // handler is the main function logic func handler(request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) { // 1. 从查询参数中获取 'name' name := request.QueryStringParameters["name"] // 2. 如果 'name' 为空,则提供一个默认值 if name == "" { name = "World" } // 3. 构建我们的响应消息 message := fmt.Sprintf("Hello, %s!", name) // 4. 将消息封装到我们的自定义 Response 结构体中 responseObject := Response{ Message: message, } // 5. 将 Response 结构体序列化为 JSON 字符串 responseBody, err := json.Marshal(responseObject) if err != nil { return nil, err } // 6. 返回一个标准的 API Gateway 响应 return &events.APIGatewayProxyResponse{ StatusCode: 200, // OK Headers: map[string]string{ "Content-Type": "application/json", }, Body: string(responseBody), }, nil } func main() { // 将我们的 handler 函数包装成一个 Lambda 服务 lambda.Start(handler) }
-
安装依赖。
aws-lambda-go
是让我们的 Go 代码能与 AWS Lambda (Netlify Functions 的底层) 兼容的关键库。bashgo get github.com/aws/aws-lambda-go/events go get github.com/aws/aws-lambda-go/lambda
运行后,
go.mod
和go.sum
文件会自动更新。
Netlify构建配置
我们需要告诉 Netlify 如何编译我们的 Go 代码。
-
在项目根目录(与
go.mod
同级)创建一个netlify.toml
文件。 -
将以下配置粘贴到
netlify.toml
文件中:toml# netlify.toml [build] # 构建命令:编译我们的 Go 函数 # -o 指定输出文件路径和名称。Netlify 会在这个目录里寻找可执行文件。 command = "go build -o netlify/functions/hello/main netlify/functions/hello/main.go" # 函数目录 functions = "netlify/functions/" [functions] # 明确指定 Go 函数的运行时 # 这有助于 Netlify 优化构建过程 included_files = ["**/*.go", "go.mod", "go.sum"]
本地测试(Netlify CLI)
在部署到线上之前,我们先用 Netlify CLI 在本地进行测试。
-
在项目根目录运行:
bashnetlify dev
-
Netlify CLI 会启动一个本地开发服务器。你会看到类似下面的输出:
text◈ Server listening on http://localhost:8888 ... ◈ Functions server is listening on 53011
-
现在,打开你的浏览器或 Postman,访问以下 URL:
-
不带参数: http://localhost:8888/.netlify/functions/hello
你应该会看到:
{"message":"Hello, World!"}
-
带参数: http://localhost:8888/.netlify/functions/hello?name=Netlify
你应该会看到:
{"message":"Hello, Netlify!"}
-
如果本地测试成功,说明我们的代码和配置都是正确的!
确认这个基本流程成功跑通后,就可以进入下一步:在 Go 函数中集成 Supabase 或Neon。
上线—部署到Netlify
最后一步,我们将它部署到互联网上。
-
初始化 Git 并推送到 GitHub:
bashgit init git add . git commit -m "feat: initial commit with hello function"
- 在 GitHub 上创建一个新的空仓库(例如
my-netlify-go-app
)。 - 按照 GitHub 提供的指令,将你的本地仓库与远程仓库关联并推送:
bash
git remote add origin https://github.com/YOUR_USERNAME/YOUR_REPO_NAME.git git branch -M main git push -u origin main
- 在 GitHub 上创建一个新的空仓库(例如
-
在 Netlify 上创建站点:
- 登录 Netlify,点击 “Add new site” -> “Import an existing project”。
- 选择 GitHub,并授权 Netlify 访问你的仓库。
- 从列表中选择你刚刚创建的
my-netlify-go-app
仓库。 - Netlify 会自动读取你的
netlify.toml
文件,所以构建设置通常无需修改。直接点击 “Deploy site”。
-
等待部署完成: Netlify 会开始拉取代码、执行
go build
命令并部署。你可以在 “Deploys” 页面看到部署日志。 -
验证线上接口:
- 部署成功后,Netlify 会给你一个独一无二的网址,例如
https://some-random-name.netlify.app
。 - 你的 Go 云函数 API 就可以通过
https://your-site-name.netlify.app/.netlify/functions/hello
访问了。 - 像本地测试一样,在浏览器中访问它,看看是否能得到正确的结果。
- 部署成功后,Netlify 会给你一个独一无二的网址,例如
Go云函数与Neon数据库集成
目标: 沿用 my-netlify-go-app
项目,在其中添加一个名为 get-products
的新 API 接口。这个接口将连接到您的 Neon 数据库,查询一个产品列表,并以 JSON 格式返回。
创建一个新的 API 端点:.../.netlify/functions/get-products
。当访问它时,它会:
- 安全地从环境变量中读取 Neon 数据库的连接信息。
- 连接到 Neon 数据库。
- 从一个
products
表中查询所有数据。 - 将查询结果以 JSON 数组的形式返回。
准备Neon数据库
根据Netlify中云函数部署的 区域(一般都是us-east-2 (Ohio)
),在Neon中创建一个处于同一区域的数据库。(不在同一个区域会导致云函数执行时间变长)
查看Netlify项目云函数部署区域的地址:
https://app.netlify.com/projects/my-netlify-app/configuration/deploys#functions-region
,注意将 my-netlify-app 替换为自己项目的名字
接下来还需要在数据库中创建一些示例数据,以便我们的函数有内容可以查询。
-
登录到您的 Neon 控制台。
-
选择您的项目,然后导航到 SQL Editor (SQL 编辑器)。
-
复制以下 SQL 代码,粘贴到编辑器中,然后点击 “Run” 执行。
sql-- 创建一个名为 'products' 的表 CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, price NUMERIC(10, 2) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- 插入一些示例数据 INSERT INTO products (name, price) VALUES ('Go Gopher Plush', 19.99), ('Netlify Sticker Pack', 4.99), ('Neon Serverless Mug', 12.50);
执行成功后,您的数据库中就有了一个包含 3 条记录的
products
表。
在 Netlify 中设置环境变量
最关键的安全步骤: 绝不能将数据库连接密码硬编码在代码里,而是通过Netlify的环境变量来管理
- 在您的 Neon 项目控制台的 Dashboard 页面,找到 Connection Details (连接详情) 部分。
- 从连接信息中,复制 Postgres connection string (它看起来像
postgres://user:password@host/dbname
)。 - 打开您的 Netlify 站点后台 (
app.netlify.com
)。 - 导航到
Projects
> 指定的项目(如:my-netlify-go-app) >Project configuration
>Environment variables
。 - 点击 “Add a variable”,选择 “Add a single variable”。
- 键 (Key) 输入:
NEON_DATABASE_URL
- 值 (Value) 粘贴:您刚刚从 Neon 复制的完整连接字符串。
- 点击 “Create variable” 保存。
现在 Go 云函数在部署后就可以通过 os.Getenv("NEON_DATABASE_URL")
来安全地访问这个值了。
更新 Go 项目
-
安装 Go Postgres 驱动: 我们需要
pgx
库,它是目前最高性能、功能最丰富的 Go Postgres 驱动。在您的项目根目录my-netlify-go-app/
中运行:bashgo get github.com/jackc/pgx/v5
-
创建新函数的目录:
bashmkdir -p netlify/functions/get-products
-
编写 Go 函数代码 (
get-products/main.go
): 在netlify/functions/get-products/
目录下创建main.go
文件,并粘贴以下代码:go// netlify/functions/get-products/main.go package main import ( "context" "encoding/json" "fmt" "os" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/jackc/pgx/v5" ) // Product 结构体,用于映射数据库中的 products 表 type Product struct { ID int `json:"id"` Name string `json:"name"` Price float64 `json:"price"` } func handler(ctx context.Context) (*events.APIGatewayProxyResponse, error) { // 1. 从环境变量中安全地读取数据库连接字符串 connString := os.Getenv("NEON_DATABASE_URL") if connString == "" { return &events.APIGatewayProxyResponse{StatusCode: 500, Body: "NEON_DATABASE_URL not set"}, nil } // 2. 连接到 Neon 数据库 conn, err := pgx.Connect(ctx, connString) if err != nil { return &events.APIGatewayProxyResponse{StatusCode: 500, Body: fmt.Sprintf("Unable to connect to database: %v", err)}, nil } defer conn.Close(ctx) // 确保函数结束时关闭连接 // 3. 执行 SQL 查询 rows, err := conn.Query(ctx, "SELECT id, name, price FROM products ORDER BY id;") if err != nil { return &events.APIGatewayProxyResponse{StatusCode: 500, Body: fmt.Sprintf("Query failed: %v", err)}, nil } defer rows.Close() // 确保处理完结果集后关闭它 // 4. 处理查询结果 var products []Product for rows.Next() { var p Product err := rows.Scan(&p.ID, &p.Name, &p.Price) if err != nil { return &events.APIGatewayProxyResponse{StatusCode: 500, Body: fmt.Sprintf("Failed to scan row: %v", err)}, nil } products = append(products, p) } // 5. 将结果序列化为 JSON responseBody, err := json.Marshal(products) if err != nil { return &events.APIGatewayProxyResponse{StatusCode: 500, Body: fmt.Sprintf("Failed to marshal response: %v", err)}, nil } // 6. 返回成功的响应 return &events.APIGatewayProxyResponse{ StatusCode: 200, Headers: map[string]string{"Content-Type": "application/json"}, Body: string(responseBody), }, nil } func main() { lambda.Start(handler) }
更新netlify配置
之前的 netlify.toml
文件只构建了一个 hello
函数。现在我们需要一个能构建所有函数的通用命令。
打开 netlify.toml
文件,将其中的 command
修改为以下内容:
# netlify.toml
[build]
# 在项目根目录执行,为每个函数目录构建一个 main 可执行文件。
command = "for dir in netlify/functions/*; do function_name=$(basename $dir); go build -o \"netlify/functions/${function_name}/main\" \"./netlify/functions/${function_name}/\"; done"
functions = "netlify/functions/"
# ... 其他配置 ...
这个命令会自动找到 netlify/functions
下的所有子目录(hello
和 get-products
),并为它们分别执行 go build
。这是一个可扩展的、一劳永逸的配置。
再次本地测试
为了在本地测试,netlify dev
命令需要能访问到数据库连接字符串。
- 在项目根目录
my-netlify-go-app/
创建一个名为.env
的文件。 - 在
.env
文件中添加以下内容,将值替换为您自己的连接字符串:textNEON_DATABASE_URL="postgres://user:password@host/dbname"
- 重要:将
.env
文件添加到.gitignore
中,防止将密码提交到 Git 仓库!text# .gitignore .env
- 现在,运行本地开发服务器:
bash
netlify dev
- 访问新接口的本地 URL:http://localhost:8888/.netlify/functions/get-products
- 您应该能看到浏览器返回了您在数据库中创建的三个产品的 JSON 数据。
本地测试成功后,就可以部署上线了。
部署到线上
- 将所有更改提交到 Git:
bash
git add . git commit -m "feat: add get-products function connecting to Neon" git push
- Netlify 会自动检测到您的代码推送,并使用
netlify.toml
中新的构建命令来部署您的两个函数。 - 部署成功后,访问您的线上 URL
https://your-site-name.netlify.app/.netlify/functions/get-products
进行最终验证。
保活Neon的定时函数
目标:创建一个名为 keep-alive
的 Netlify 云函数,它没有公开的 API 接口,而是由 Netlify 的服务器根据预设的时间表自动在后台触发。它的唯一目的就是定期向 Neon 数据库发送一个极轻量的查询,防止数据库因闲置而进入休眠状态。
创建新函数的目录
在 my-netlify-go-app
项目中,为新函数创建一个目录。
- 打开终端,进入项目根目录。
- 执行命令:
bash
mkdir -p netlify/functions/keep-alive
项目结构现在看起来像这样:
/my-netlify-go-app
|-- ... (go.mod, netlify.toml, etc.)
|-- netlify/
|-- functions/
|-- hello/
|-- get-products/
|-- keep-alive/ <-- 新创建的目录
编写保活函数代码
这个函数的代码与 get-products
非常相似,但更简单,因为它不需要处理 HTTP 请求和响应。
-
在
netlify/functions/keep-alive/
目录下创建一个main.go
文件。 -
将以下代码复制并粘贴到
main.go
文件中:go// netlify/functions/keep-alive/main.go package main import ( "context" "fmt" "log" "os" "github.com/aws/aws-lambda-go/lambda" "github.com/jackc/pgx/v5" ) // handleRequest是函数的核心逻辑。 // 注意:它的签名比API函数更简单,因为它不处理HTTP事件。 func handleRequest(ctx context.Context) error { log.Println("Running keep-alive function to ping Neon database...") // 从环境变量安全地读取数据库连接字符串 connString := os.Getenv("NEON_DATABASE_URL") if connString == "" { log.Println("Error: NEON_DATABASE_URL environment variable not set.") return fmt.Errorf("NEON_DATABASE_URL not set") } // 连接数据库 conn, err := pgx.Connect(ctx, connString) if err != nil { log.Printf("Error: Unable to connect to database: %v\n", err) return err } defer conn.Close(ctx) // 执行一个最轻量的查询,目的仅仅是“触摸”一下数据库 var result int err = conn.QueryRow(ctx, "SELECT 1;").Scan(&result) if err != nil { log.Printf("Error: Keep-alive query failed: %v\n", err) return err } // 成功的日志对于验证至关重要 log.Printf("Keep-alive ping to Neon successful. Query result: %d", result) return nil } func main() { // 启动 Lambda 处理程序 lambda.Start(handleRequest) }
代码要点:
- 日志 (Logging):我们使用了
log
包来打印信息。因为这个函数没有 API 响应,查看日志是验证它是否成功运行的唯一方式。 - 轻量查询:
SELECT 1;
是最理想的保活查询。它几乎不消耗任何数据库资源,但足以让数据库认为自己是“活跃”的。 - 简单的 Handler:函数签名是
func(context.Context) error
,因为它由调度器触发,而不是 HTTP 请求。
配置定时调度
这是将普通函数变为“定时函数”的关键一步。
-
打开项目根目录下的
netlify.toml
文件。 -
不需要修改
[build]
部分的command
,之前设置的for
循环命令会自动构建这个新的keep-alive
函数。 -
在文件的末尾,添加以下新的配置块:
toml# netlify.toml # 现有的 [build] 配置 ... [functions."keep-alive"] # 调度计划: # - Cron 表达式: "*/5 1-15 * * *" # - UTC 时间: 每天的 01:00 到 15:59 之间,每5分钟执行一次。 # - 对应东八区时间 (UTC+8): 每天的 09:00 到 23:59 之间,每5分钟执行一次。 schedule = "*/5 1-15 * * *"
配置解释:
[functions."keep-alive"]
: 这个特殊的 TOML 语法表示我们正在为名为keep-alive
的函数进行特定配置。- Netlify 的定时函数调度器(以及几乎所有的国际通用 Cron 系统)都使用 UTC (协调世界时) 来解析 Cron 表达式
- 需要的时间 (东八区, UTC+8): 每天的 09:00 到 23:59 转换到 UTC 时间:
- 开始时间:东八区 09:00 = 09:00 - 8小时 = UTC 01:00
- 结束时间:东八区 23:59 = 23:59 - 8小时 = UTC 15:59
- 得到的 Cron 表达式是:
*/5 1-15 * * *
- 需要的时间 (东八区, UTC+8): 每天的 09:00 到 23:59 转换到 UTC 时间:
部署与验证
-
部署: 将所有更改提交到您的 Git 仓库:
bashgit add . git commit -m "feat: add keep-alive scheduled function for Neon" git push
Netlify 将自动开始部署。
-
验证 (最重要的一步): 由于这个函数没有 URL 可以访问,我们需要通过查看日志来验证它是否在按计划运行。
- 部署成功后,等待几分钟(最多等 4-5 分钟,让第一个调度周期命中)。
- 登录到您的 Netlify 站点后台。
- 点击顶部的 Functions 标签页。
- 在函数列表中,点击您新创建的
keep-alive
函数。 - 在函数详情页面,查看下方的 Function log (函数日志) 部分。
您应该能看到类似下面的日志条目,并且每隔大约 4 分钟就会出现一条新的:
text... INFO: Running keep-alive function to ping Neon database... ... INFO: Keep-alive ping to Neon successful. Query result: 1
一旦这个 keep-alive
定时函数开始稳定运行,就可以避免 Neon 数据库5分钟不活动后进入休眠状态