EdgeOne Makers Cloud Functions Go
Go 语言天生擅长高并发和高性能场景,编译成单个二进制文件,启动快、内存占用低。EdgeOne Makers 的 Cloud Functions 支持 Go 运行时,适合那些对性能有要求的后端逻辑。这一篇讲 Go 函数的开发流程、依赖管理和完整示例。
项目结构
Go Cloud Function 的项目结构如下。
my-function/
├── index.go # 入口文件
├── go.mod # 依赖声明
├── go.sum # 依赖校验文件(自动生成)
└── lib/ # 其他包(可选)
└── utils.go
index.go 是入口文件,go.mod 声明模块和依赖。
入口函数
入口函数固定叫 MainHandler,接收 context.Context 和 json.RawMessage 两个参数。
// index.go
package main
import (
"context"
"encoding/json"
)
// Response 返回值结构
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body"`
}
// MainHandler 入口函数
func MainHandler(ctx context.Context, event json.RawMessage) (*Response, error) {
body, _ := json.Marshal(map[string]string{
"message": "Hello Go Cloud Function",
})
return &Response{
StatusCode: 200,
Headers: map[string]string{
"content-type": "application/json; charset=utf-8",
},
Body: string(body),
}, nil
}
返回值是一个 *Response 指针和 error。
| 字段 | 类型 | 说明 |
|---|---|---|
| StatusCode | int | HTTP 状态码 |
| Headers | map[string]string | 响应头 |
| Body | string | 响应体 |
| IsBase64Encoded | bool | body 是否经过 base64 编码 |
解析 event 参数
event 的类型是 json.RawMessage,也就是一段原始的 JSON 字节。你需要自己定义结构体来解析它。
// index.go
package main
import (
"context"
"encoding/json"
)
// Event HTTP 触发时的 event 结构
type Event struct {
HTTPMethod string `json:"httpMethod"`
Path string `json:"path"`
Headers map[string]string `json:"headers"`
QueryStringParameters map[string]string `json:"queryStringParameters"`
Body string `json:"body"`
PathParameters map[string]string `json:"pathParameters"`
}
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body"`
}
func MainHandler(ctx context.Context, event json.RawMessage) (*Response, error) {
// 解析 event
var e Event
json.Unmarshal(event, &e)
// 读取请求信息
method := e.HTTPMethod
path := e.Path
keyword := e.QueryStringParameters["keyword"]
body, _ := json.Marshal(map[string]string{
"method": method,
"path": path,
"keyword": keyword,
})
return &Response{
StatusCode: 200,
Headers: map[string]string{
"content-type": "application/json; charset=utf-8",
},
Body: string(body),
}, nil
}
处理 HTTP 请求
处理 POST 请求的 JSON 数据
// index.go
package main
import (
"context"
"encoding/json"
)
type Event struct {
HTTPMethod string `json:"httpMethod"`
Body string `json:"body"`
}
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body"`
}
// PostBody POST 请求体的结构
type PostBody struct {
Name string `json:"name"`
Age int `json:"age"`
}
func MainHandler(ctx context.Context, event json.RawMessage) (*Response, error) {
var e Event
json.Unmarshal(event, &e)
if e.HTTPMethod != "POST" {
errBody, _ := json.Marshal(map[string]string{"error": "只接受 POST 请求"})
return &Response{StatusCode: 405, Body: string(errBody)}, nil
}
// 解析请求体
var data PostBody
err := json.Unmarshal([]byte(e.Body), &data)
if err != nil {
errBody, _ := json.Marshal(map[string]string{"error": "请求体 JSON 格式不对"})
return &Response{StatusCode: 400, Body: string(errBody)}, nil
}
// 构造返回
respBody, _ := json.Marshal(map[string]string{
"message": "收到数据, 名字: " + data.Name,
})
return &Response{
StatusCode: 200,
Headers: map[string]string{"content-type": "application/json; charset=utf-8"},
Body: string(respBody),
}, nil
}
读取环境变量
Go 里用 os.Getenv 读取环境变量。
// index.go
package main
import (
"context"
"encoding/json"
"os"
)
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body"`
}
func MainHandler(ctx context.Context, event json.RawMessage) (*Response, error) {
apiKey := os.Getenv("API_KEY")
dbHost := os.Getenv("DB_HOST")
if apiKey == "" {
errBody, _ := json.Marshal(map[string]string{"error": "缺少 API_KEY 环境变量"})
return &Response{StatusCode: 500, Body: string(errBody)}, nil
}
body, _ := json.Marshal(map[string]interface{}{
"dbHost": dbHost,
"apiKeySet": true,
})
return &Response{
StatusCode: 200,
Headers: map[string]string{"content-type": "application/json; charset=utf-8"},
Body: string(body),
}, nil
}
依赖管理
使用 go mod
# 初始化模块
go mod init my-cloud-function
# 添加依赖
go get github.com/google/uuid
# 整理依赖
go mod tidy
生成的 go.mod 文件长这样。
module my-cloud-function
go 1.21
require github.com/google/uuid v1.6.0
部署时平台会下载依赖并编译。你也可以本地先编译好再部署。
使用标准库
Go 的标准库覆盖面很广,很多场景不需要第三方依赖。
// index.go
package main
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
)
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body"`
}
func MainHandler(ctx context.Context, event json.RawMessage) (*Response, error) {
// 生成随机 token
b := make([]byte, 16)
rand.Read(b)
token := hex.EncodeToString(b)
body, _ := json.Marshal(map[string]string{
"token": token,
})
return &Response{
StatusCode: 200,
Headers: map[string]string{"content-type": "application/json; charset=utf-8"},
Body: string(body),
}, nil
}
完整示例: 待办事项 API
下面用 Go 写一个待办事项接口,功能和 Node.js 版本、Python 版本一致。
// index.go
package main
import (
"context"
"encoding/json"
"strconv"
"strings"
"time"
)
// Todo 待办事项
type Todo struct {
ID int `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
CreatedAt string `json:"createdAt"`
}
// Event HTTP 触发 event
type Event struct {
HTTPMethod string `json:"httpMethod"`
Path string `json:"path"`
Body string `json:"body"`
PathParameters map[string]string `json:"pathParameters"`
}
type Response struct {
StatusCode int `json:"statusCode"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body"`
}
// 全局状态(演示用,实际应该用数据库)
var (
todos []Todo
nextID int = 1
)
func MainHandler(ctx context.Context, event json.RawMessage) (*Response, error) {
var e Event
json.Unmarshal(event, &e)
// 查询全部
if e.Path == "/api/todos" && e.HTTPMethod == "GET" {
body, _ := json.Marshal(map[string]interface{}{"data": todos})
return &Response{StatusCode: 200, Body: string(body)}, nil
}
// 新增一条
if e.Path == "/api/todos" && e.HTTPMethod == "POST" {
var data struct {
Title string `json:"title"`
}
json.Unmarshal([]byte(e.Body), &data)
if strings.TrimSpace(data.Title) == "" {
errBody, _ := json.Marshal(map[string]string{"error": "title 不能为空"})
return &Response{StatusCode: 400, Body: string(errBody)}, nil
}
newTodo := Todo{
ID: nextID,
Title: data.Title,
Done: false,
CreatedAt: time.Now().Format(time.RFC3339),
}
nextID++
todos = append(todos, newTodo)
body, _ := json.Marshal(map[string]interface{}{"data": newTodo})
return &Response{StatusCode: 201, Body: string(body)}, nil
}
// 删除一条
if strings.HasPrefix(e.Path, "/api/todos/") && e.HTTPMethod == "DELETE" {
idStr := e.PathParameters["id"]
id, _ := strconv.Atoi(idStr)
found := -1
for i, t := range todos {
if t.ID == id {
found = i
break
}
}
if found == -1 {
errBody, _ := json.Marshal(map[string]string{"error": "找不到这条待办"})
return &Response{StatusCode: 404, Body: string(errBody)}, nil
}
// 删除
todos = append(todos[:found], todos[found+1:]...)
body, _ := json.Marshal(map[string]string{"message": "删除成功"})
return &Response{StatusCode: 200, Body: string(body)}, nil
}
errBody, _ := json.Marshal(map[string]string{"error": "路由不存在"})
return &Response{StatusCode: 404, Body: string(errBody)}, nil
}
三种运行时写法对比
| 对比项 | Node.js | Python | Go |
|---|---|---|---|
| 入口函数 | exports.main |
def main_handler |
func MainHandler |
| event 类型 | Object | dict | json.RawMessage |
| 解析 JSON | JSON.parse() | json.loads() | json.Unmarshal() |
| 序列化 JSON | JSON.stringify() | json.dumps() | json.Marshal() |
| 环境变量 | process.env.KEY | os.environ.get(‘KEY’) | os.Getenv(“KEY”) |
| 依赖管理 | package.json | requirements.txt | go.mod |
| 编译 | 不需要(解释执行) | 不需要(解释执行) | 需要编译成二进制 |
Go 的强类型和编译特性让代码写起来稍微繁琐一些,但换来的是更好的性能和更少的运行时错误。
速查卡片
| 要点 | 说明 |
|---|---|
| 入口函数 | func MainHandler(ctx context.Context, event json.RawMessage) |
| event 解析 | 先定义结构体,再用 json.Unmarshal 解析 |
| 返回值 | (*Response, error),Response 含 StatusCode/Headers/Body |
| 依赖管理 | go.mod 声明,部署时自动下载并编译 |
| 环境变量 | os.Getenv(“KEY”) 读取 |
| JSON 处理 | json.Marshal() 序列化,json.Unmarshal() 反序列化 |
| 调试方式 | fmt.Println() 输出,控制台日志模块查看 |
| 优势 | 编译成二进制,启动快,内存占用低,适合高性能场景 |