跳到主要内容

HTTP 云函数路由

基于 @cloudbase/functions-framework 框架可以将一个大函数拆分成多个子函数,并通过请求路径将不同的请求路由到不同的处理函数

为什么需要函数路由

传统的云函数开发模式中,每个函数都需要独立部署和运行,这会带来以下问题:

  • 资源浪费:每个函数独占一个实例,即使请求量很小也需要保持运行
  • 冷启动频繁:多个函数意味着更多的冷启动次数
  • 部署复杂:需要分别部署和管理多个函数
  • 成本较高:更多的实例数量带来更高的资源成本

使用函数路由可以解决这些问题:

  • 资源共享:多个函数共享同一实例,充分利用计算资源
  • 降低成本:减少实例数量,降低冷启动次数
  • 简化部署:一次部署多个函数,统一管理
  • 提升性能:共享内存和连接池等资源,提高响应速度

前置条件

使用函数路由功能需要安装 @cloudbase/functions-framework 依赖:

npm install @cloudbase/functions-framework

该框架提供了函数加载、路由分发、请求处理等核心能力。

快速开始

第一步:创建项目结构

创建一个支持多函数的项目目录:

my-functions/
├── package.json # 项目配置
├── scf_bootstrap # 启动脚本
├── cloudbase-functions.json # 函数路由配置(关键)
├── index.js # 默认函数入口
├── user/ # user 函数目录
│ └── index.js
└── order/ # order 函数目录
└── index.js

第二步:创建函数代码

默认函数(index.js)

exports.main = function (event, context) {
return {
message: 'Hello from default function',
path: context.httpContext.url,
};
};

user 函数(user/index.js)

exports.main = function (event, context) {
return {
message: 'User API',
path: context.httpContext.url,
method: context.httpContext.httpMethod,
};
};

order 函数(order/index.js)

exports.main = function (event, context) {
return {
message: 'Order API',
path: context.httpContext.url,
};
};

第三步:配置函数路由

创建 cloudbase-functions.json 配置文件:

{
"functionsRoot": ".",
"functions": [
{
"name": "default",
"directory": "."
},
{
"name": "user",
"directory": "user",
"triggerPath": "/api/user" // triggerPath 是 routes 的简化写法,可以直接在函数定义中指定触发路径
},
{
"name": "order",
"directory": "order",
"triggerPath": "/api/order"
}
],
"routes": [
{
"functionName": "echo",
"path": "/echo"
}
]
}

第四步:配置 package.json

{
"name": "my-functions",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@cloudbase/functions-framework": "latest"
}
}

第五步:创建启动脚本

创建 scf_bootstrap 文件(无扩展名):

#!/bin/bash
node node_modules/@cloudbase/functions-framework/bin/tcb-ff.js \
--port=9000 \
--logDirname=/tmp/logs \
--interceptOutput=false \
--extendedContextKey=X-Cloudbase-Context \
--functionsConfigFile=cloudbase-functions.json

⚠️ 重要scf_bootstrap 文件需要具有可执行权限。在 Linux/macOS 系统中使用 chmod +x scf_bootstrap 命令设置权限。

启动参数说明

参数说明默认值
--port函数监听端口9000
--logDirname日志存储目录/tmp/logs
--interceptOutput是否拦截标准输出false
--extendedContextKey扩展上下文请求头键名X-Cloudbase-Context
--functionsConfigFile函数路由配置文件路径cloudbase-functions.json

第六步:安装依赖

npm install

第七步:本地测试

启动函数服务:

./scf_bootstrap

测试不同路由:

# 测试默认函数
curl http://localhost:9000/

# 测试 user 函数
curl http://localhost:9000/api/user

# 测试 order 函数
curl http://localhost:9000/api/order

第八步:部署到云端

使用云开发 CLI 或控制台部署函数,部署后访问:

# 默认函数
https://your-function.run.tcloudbase.com/

# user 函数
https://your-function.run.tcloudbase.com/api/user

# order 函数
https://your-function.run.tcloudbase.com/api/order

路由匹配规则

理解路由匹配规则对于正确配置函数路由至关重要:

1. 前缀匹配

路由匹配采用前缀匹配模式。当请求路径以某个路由规则的 path 开头时,即可匹配该路由。

示例

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

以下请求路径都会匹配到 user 函数:

  • /api/user
  • /api/user/
  • /api/user/profile
  • /api/user/list

以下请求路径不会匹配:

  • /api/users ❌(不是以 /api/user 开头)
  • /api ❌(路径不完整)

2. 最长匹配

当多个路由规则都能匹配同一请求路径时,框架会选择最长的那个路由规则。

示例

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
},
{
"functionName": "userProfile",
"path": "/api/user/profile"
}
]
}

路由结果:

  • /api/useruser 函数
  • /api/user/listuser 函数
  • /api/user/profileuserProfile 函数(最长匹配)
  • /api/user/profile/detailuserProfile 函数(最长匹配)

3. 末尾斜杠处理

路径末尾的 / 不影响匹配结果,/api/user/api/user/ 被视为等价。

示例

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

以下路径都会匹配:

  • /api/user
  • /api/user/

4. 路径单元精确匹配

路由匹配以 / 分隔为路径单元,每个路径单元需要精确匹配

示例

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

路由结果:

  • /api/user
  • /api/users ❌(usersuser 不匹配)
  • /api/user123 ❌(user123user 不匹配)

5. 一函数多路由

一个函数可以配置多个触发路径,从不同的 URL 路径访问同一个函数。

示例

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
},
{
"functionName": "user",
"path": "/v1/user"
},
{
"functionName": "user",
"path": "/v2/user"
}
]
}

以上三个路径都会路由到 user 函数。

6. 一路由一函数

同一个路径只能指向一个函数,不能同时指向多个函数。

错误示例

{
"routes": [
{
"functionName": "userA",
"path": "/api/user"
},
{
"functionName": "userB",
"path": "/api/user"
}
]
}

⚠️ 注意:上述配置会导致路由冲突,框架会报错。

7. 默认路由

可以配置一个 path/ 的路由作为默认路由,处理所有未匹配的请求。

示例

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
},
{
"functionName": "order",
"path": "/api/order"
},
{
"functionName": "default",
"path": "/"
}
]
}

路由结果:

  • /api/useruser 函数
  • /api/orderorder 函数
  • /default 函数
  • /aboutdefault 函数
  • /healthdefault 函数

进阶用法

使用 triggerPath 简化配置

triggerPathroutes 的简化写法,可以直接在函数定义中指定触发路径。

对比

使用 triggerPath

{
"functions": [
{
"name": "user",
"directory": "user",
"triggerPath": "/api/user"
}
],
"routes": []
}

等价于:

{
"functions": [
{
"name": "user",
"directory": "user"
}
],
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

💡 提示:推荐使用 triggerPath,配置更简洁。如果需要一个函数对应多个路由,则需要使用 routes 配置。

结合 Express/Koa 框架

函数路由可以与 Express、Koa 等 Web 框架结合使用,每个子函数可以是一个完整的 Web 应用。

示例

// user/index.js
const express = require('express');
const app = express();

app.get('/profile', (req, res) => {
res.json({ user: 'profile' });
});

app.get('/list', (req, res) => {
res.json({ users: [] });
});

exports.main = app;

访问:

  • https://your-function.api.tcloudbasegateway.com/v1/functions/{function-name}/api/user/profile?webfn=true
  • https://your-function.api.tcloudbasegateway.com/v1/functions/{function-name}/api/user/list?webfn=true

混合使用 SSE 和 WebSocket

不同的子函数可以处理不同类型的请求,比如一个函数处理 HTTP 请求,另一个处理 WebSocket 连接。

示例

.
├── cloudbase-functions.json
├── index.js # HTTP 函数
└── ws/ # WebSocket 函数
└── index.js

cloudbase-functions.json

{
"functionsRoot": ".",
"functions": [
{
"name": "default",
"directory": "."
},
{
"name": "ws",
"directory": "ws",
"triggerPath": "/ws"
}
]
}

VSCode 编辑器支持

安装 @cloudbase/functions-framework 依赖后,可以在 VSCode 中获得 cloudbase-functions.json 的 schema 自动提示。

在项目根目录创建 .vscode/settings.json 文件:

{
"json.schemas": [
{
"fileMatch": ["cloudbase-functions.json"],
"url": "./node_modules/@cloudbase/functions-framework/functions-schema.json"
}
]
}

配置后,在编辑 cloudbase-functions.json 文件时会自动提示字段和类型。

完整示例

项目结构

my-api-service/
├── .vscode/
│ └── settings.json # VSCode 配置
├── package.json # 项目配置
├── scf_bootstrap # 启动脚本
├── cloudbase-functions.json # 函数路由配置
├── index.js # 默认函数
├── user/ # 用户 API
│ └── index.js
├── order/ # 订单 API
│ └── index.js
├── product/ # 商品 API
│ └── index.js
└── sse/ # SSE 流式响应
└── index.js

cloudbase-functions.json

{
"functionsRoot": ".",
"functions": [
{
"name": "default",
"directory": ".",
"triggerPath": "/"
},
{
"name": "user",
"directory": "user",
"triggerPath": "/api/user"
},
{
"name": "order",
"directory": "order",
"triggerPath": "/api/order"
},
{
"name": "product",
"directory": "product",
"triggerPath": "/api/product"
},
{
"name": "sse",
"directory": "sse",
"triggerPath": "/stream"
}
]
}

常见问题

1. 路由配置不生效

问题:配置了路由规则,但访问时仍然路由到错误的函数。

解决方案

  • 检查 cloudbase-functions.json 文件是否在正确的位置
  • 检查 scf_bootstrap 启动脚本中是否指定了 --functionsConfigFile 参数
  • 检查路由规则的顺序,确保最长匹配规则在前
  • 使用 console.log 输出 context.httpContext.url 查看实际请求路径

2. 函数找不到

问题:启动时报错:Function xxx not found

解决方案

  • 检查 functions[].directory 路径是否正确
  • 检查 functions[].source 文件是否存在
  • 检查 functions[].target 导出的函数名是否正确
  • 确保 functionsRoot 设置正确

3. 路由冲突

问题:启动时报错:Route conflict

解决方案

  • 检查是否有多个路由规则指向同一路径
  • 检查 triggerPathroutes 中是否有重复配置
  • 确保每个路径只对应一个函数

4. 默认函数不生效

问题:配置了 path: "/" 的默认路由,但访问时返回 404。

解决方案

  • 确保默认路由配置在 routes 数组的最后
  • 检查是否有其他路由规则覆盖了默认路由
  • 确保默认函数的 directorysource 配置正确

5. 本地开发时热更新不生效

问题:修改函数代码后,需要重启服务才能生效。

解决方案

@cloudbase/functions-framework 框架暂不支持热更新,需要手动重启服务:

# 停止服务(Ctrl+C)
# 重新启动
./scf_bootstrap

相关文档