六维教程

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.Contextjson.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() 输出,控制台日志模块查看
优势 编译成二进制,启动快,内存占用低,适合高性能场景
上一篇
EdgeOne Makers Cloud Functions Python
下一篇
EdgeOne Makers 中间件