在云函数中调用其他云函数
在云函数开发中,您可能需要在一个云函数中调用另一个云函数,以实现功能模块化、代码复用和业务逻辑分离。CloudBase 支持在云函数中调用当前环境或其他环境下的云函数。
应用场景
典型使用场景
- 功能模块化:将复杂业务拆分为多个独立的云函数
- 代码复用:共享通用的业务逻辑和工具函数
- 权限隔离:不同权限级别的操作分别处理
- 异步处理:触发后台任务和异步操作
- 跨环境调用:开发、测试、生产环境间的函数调用
实际应用示例
// 订单处理函数调用多个子函数
exports.main = async (event, context) => {
// 1. 验证用户权限
const authResult = await app.callFunction({
name: 'user-auth',
data: { userId: event.userId, action: 'create_order' }
});
// 2. 计算订单金额
const priceResult = await app.callFunction({
name: 'price-calculator',
data: { items: event.items, couponCode: event.couponCode }
});
// 3. 发送通知
await app.callFunction({
name: 'notification-sender',
data: { type: 'order_created', orderId: orderId }
});
return { success: true, orderId: orderId };
};
调用方式概览
📄️ Node.js SDK 调用
在云函数中使用 Node.js SDK 调用其他云函数
📄️ HTTP API 调用
通过 HTTP API 方式调用云函数
📄️ 跨环境调用
调用不同环境下的云函数
使用 Node.js SDK 调用
Node.js SDK 是在云函数中调用其他云函数的推荐方式,提供了简洁的 API 和完善的错误处理。
基础调用
- 同环境调用
- 跨环境调用
const tcb = require('@cloudbase/node-sdk');
exports.main = async (event, context) => {
// 初始化 SDK(自动获取当前环境信息)
const app = tcb.init({
env: tcb.SYMBOL_CURRENT_ENV // 使用当前环境
});
try {
// 调用同环境下的云函数
const result = await app.callFunction({
name: 'target-function',
data: {
message: 'Hello from caller function',
timestamp: Date.now(),
userId: event.userId
}
});
console.log('函数调用成功:', result);
return {
success: true,
data: result.result,
requestId: result.requestId
};
} catch (error) {
console.error('函数调用失败:', error);
return {
success: false,
error: error.message,
code: error.code
};
}
};
const tcb = require('@cloudbase/node-sdk');
exports.main = async (event, context) => {
// 初始化 SDK 指定目标环境
const app = tcb.init({
env: 'target-env-id', // 目标环境 ID
secretId: process.env.SECRET_ID, // 从环境变量获取
secretKey: process.env.SECRET_KEY // 从环境变量获取
});
try {
const result = await app.callFunction({
name: 'cross-env-function',
data: {
sourceEnv: tcb.SYMBOL_CURRENT_ENV,
message: event.message
}
});
return result.result;
} catch (error) {
console.error('跨环境调用失败:', error);
throw error;
}
};
高级功能
版本控制调用
exports.main = async (event, context) => {
const app = tcb.init({
env: tcb.SYMBOL_CURRENT_ENV
});
// 调用指定版本的云函数
const result = await app.callFunction({
name: 'versioned-function',
data: event.data,
qualifier: '2' // 调用版本 2
});
return result.result;
};
超时控制
exports.main = async (event, context) => {
const app = tcb.init({
env: tcb.SYMBOL_CURRENT_ENV
});
try {
// 设置 5 秒超时
const result = await app.callFunction({
name: 'slow-function',
data: event.data
}, {
timeout: 5000 // 5 秒超时
});
return result.result;
} catch (error) {
if (error.code === 'TIMEOUT') {
return { error: '函数调用超时' };
}
throw error;
}
};
批量调用
exports.main = async (event, context) => {
const app = tcb.init({
env: tcb.SYMBOL_CURRENT_ENV
});
// 并发调用多个函数
const promises = event.tasks.map(task =>
app.callFunction({
name: 'worker-function',
data: task
}).catch(error => ({ error: error.message }))
);
const results = await Promise.allSettled(promises);
return {
total: results.length,
success: results.filter(r => r.status === 'fulfilled').length,
failed: results.filter(r => r.status === 'rejected').length,
results: results.map(r =>
r.status === 'fulfilled' ? r.value.result : r.reason
)
};
};
函数型云托管调用
exports.main = async (event, context) => {
const app = tcb.init({
env: tcb.SYMBOL_CURRENT_ENV
});
// 调用函数型云托管服务
const result = await app.callFunction({
name: 'cloudrun-service',
type: 'cloudrun', // 指定为云托管类型
method: 'POST', // HTTP 方法
path: '/api/users', // 请求路径
header: { // 请求头
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
},
data: { // 请求体
name: event.userName,
email: event.userEmail
}
}, {
timeout: 10000 // 10 秒超时
});
return result.result;
};
使用 HTTP API 调用
通过 HTTP API 方式调用云函数,适用于需要更细粒度控制或与外部系统集成的场景。
基础 HTTP 调用
const https = require('https');
exports.main = async (event, context) => {
const accessToken = await getAccessToken(); // 获取访问令牌
const postData = JSON.stringify({
message: 'Hello from HTTP caller',
data: event.data
});
const options = {
hostname: `${process.env.TCB_ENV}.api.tcloudbasegateway.com`,
port: 443,
path: '/v1/functions/target-function',
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
const result = JSON.parse(data);
resolve(result);
} catch (error) {
reject(error);
}
});
});
req.on('error', (error) => {
reject(error);
});
req.write(postData);
req.end();
});
};
// 获取访问令牌的辅助函数
async function getAccessToken() {
// 实现获取 access_token 的逻辑
// 可以从环境变量、数据库或其他安全存储中获取
return process.env.ACCESS_TOKEN;
}
使用 Axios 简化 HTTP 调用
const axios = require('axios');
exports.main = async (event, context) => {
try {
const response = await axios.post(
`https://${process.env.TCB_ENV}.api.tcloudbasegateway.com/v1/functions/target-function`,
{
message: 'Hello from Axios caller',
data: event.data
},
{
headers: {
'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`,
'Content-Type': 'application/json'
},
timeout: 10000 // 10 秒超时
}
);
return {
success: true,
data: response.data,
status: response.status
};
} catch (error) {
console.error('HTTP 调用失败:', error.response?.data || error.message);
return {
success: false,
error: error.response?.data?.message || error.message,
status: error.response?.status
};
}
};
跨环境调用
跨环境调用允许您在不同的 CloudBase 环境之间调用云函数,常用于开发、测试、生产环境的数据同步和业务协作。
配置跨环境调用
const tcb = require('@cloudbase/node-sdk');
exports.main = async (event, context) => {
// 根据不同场景选择目标环境
const targetEnv = getTargetEnv(event.scenario);
const app = tcb.init({
env: targetEnv,
secretId: process.env.CROSS_ENV_SECRET_ID,
secretKey: process.env.CROSS_ENV_SECRET_KEY
});
const result = await app.callFunction({
name: 'sync-data',
data: {
sourceEnv: process.env.TCB_ENV,
syncType: event.syncType,
data: event.data
}
});
return result.result;
};
function getTargetEnv(scenario) {
const envMapping = {
'dev-to-test': 'test-env-id',
'test-to-prod': 'prod-env-id',
'prod-to-backup': 'backup-env-id'
};
return envMapping[scenario] || 'default-env-id';
}
环境变量管理
在云函数配置中设置环境变量:
# 在云开发控制台的云函数配置中添加环境变量
CROSS_ENV_SECRET_ID=your-secret-id
CROSS_ENV_SECRET_KEY=your-secret-key
TARGET_ENV_ID=target-environment-id
ACCESS_TOKEN=your-access-token
错误处理和调试
完善的错误处理
const tcb = require('@cloudbase/node-sdk');
exports.main = async (event, context) => {
const app = tcb.init({
env: tcb.SYMBOL_CURRENT_ENV
});
try {
const result = await app.callFunction({
name: 'target-function',
data: event.data
});
return {
success: true,
data: result.result,
requestId: result.requestId
};
} catch (error) {
console.error('函数调用失败:', {
code: error.code,
message: error.message,
requestId: error.requestId,
stack: error.stack
});
// 根据错误类型进行不同处理
switch (error.code) {
case 'FUNCTION_NOT_FOUND':
return {
success: false,
error: '目标函数不存在',
code: 'FUNCTION_NOT_FOUND'
};
case 'PERMISSION_DENIED':
return {
success: false,
error: '权限不足',
code: 'PERMISSION_DENIED'
};
case 'TIMEOUT':
return {
success: false,
error: '函数调用超时',
code: 'TIMEOUT'
};
default:
return {
success: false,
error: error.message || '未知错误',
code: error.code || 'UNKNOWN_ERROR'
};
}
}
};
调试技巧
exports.main = async (event, context) => {
const app = tcb.init({
env: tcb.SYMBOL_CURRENT_ENV
});
// 添加调试信息
console.log('调用开始:', {
caller: context.function_name,
target: 'target-function',
requestId: context.request_id,
timestamp: new Date().toISOString()
});
const startTime = Date.now();
try {
const result = await app.callFunction({
name: 'target-function',
data: {
...event.data,
_debug: {
caller: context.function_name,
callTime: new Date().toISOString()
}
}
});
const duration = Date.now() - startTime;
console.log('调用成功:', {
duration: `${duration}ms`,
resultSize: JSON.stringify(result.result).length,
requestId: result.requestId
});
return result.result;
} catch (error) {
const duration = Date.now() - startTime;
console.error('调用失败:', {
duration: `${duration}ms`,
error: error.message,
code: error.code
});
throw error;
}
};
性能优化建议
1. SDK 实例复用
// ❌ 错误做法:每次调用都初始化
exports.main = async (event, context) => {
const app = tcb.init({ env: tcb.SYMBOL_CURRENT_ENV });
return await app.callFunction({ name: 'target', data: event });
};
// ✅ 正确做法:复用 SDK 实例
const tcb = require('@cloudbase/node-sdk');
const app = tcb.init({ env: tcb.SYMBOL_CURRENT_ENV });
exports.main = async (event, context) => {
return await app.callFunction({ name: 'target', data: event });
};
2. 并发控制
// 限制并发调用数量
class ConcurrencyController {
constructor(maxConcurrent = 5) {
this.maxConcurrent = maxConcurrent;
this.running = 0;
this.queue = [];
}
async execute(fn) {
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject });
this.process();
});
}
async process() {
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
return;
}
this.running++;
const { fn, resolve, reject } = this.queue.shift();
try {
const result = await fn();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.process();
}
}
}
const controller = new ConcurrencyController(3);
exports.main = async (event, context) => {
const tasks = event.tasks.map(task =>
() => app.callFunction({ name: 'worker', data: task })
);
const results = await Promise.all(
tasks.map(task => controller.execute(task))
);
return results;
};
3. 缓存策略
// 简单的内存缓存
const cache = new Map();
async function cachedCallFunction(functionName, data, ttl = 60000) {
const cacheKey = `${functionName}_${JSON.stringify(data)}`;
const cached = cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < ttl) {
console.log('缓存命中:', cacheKey);
return cached.result;
}
const result = await app.callFunction({
name: functionName,
data: data
});
cache.set(cacheKey, {
result: result.result,
timestamp: Date.now()
});
return result.result;
}
最佳实践
1. 函数设计原则
// 设计可复用的工具函数
exports.main = async (event, context) => {
const { action, data } = event;
switch (action) {
case 'validate_user':
return await validateUser(data);
case 'send_notification':
return await sendNotification(data);
case 'calculate_price':
return await calculatePrice(data);
default:
throw new Error(`不支持的操作: ${action}`);
}
};
async function validateUser(userData) {
// 用户验证逻辑
return { valid: true, userId: userData.id };
}
async function sendNotification(notificationData) {
// 通知发送逻辑
return { sent: true, messageId: 'msg_123' };
}
async function calculatePrice(priceData) {
// 价格计算逻辑
return { total: 100, discount: 10 };
}
2. 错误重试机制
async function callFunctionWithRetry(functionName, data, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
const result = await app.callFunction({
name: functionName,
data: data
});
return result.result;
} catch (error) {
lastError = error;
// 只对特定错误进行重试
if (error.code === 'TIMEOUT' || error.code === 'NETWORK_ERROR') {
console.log(`重试 ${i + 1}/${maxRetries}:`, error.message);
await sleep(1000 * (i + 1)); // 指数退避
continue;
}
// 其他错误直接抛出
throw error;
}
}
throw lastError;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
3. 监控和日志
exports.main = async (event, context) => {
const startTime = Date.now();
const callId = `${context.request_id}_${Date.now()}`;
console.log('函数调用开始:', {
callId,
caller: context.function_name,
target: event.targetFunction,
timestamp: new Date().toISOString()
});
try {
const result = await app.callFunction({
name: event.targetFunction,
data: {
...event.data,
_meta: {
callId,
caller: context.function_name,
timestamp: new Date().toISOString()
}
}
});
const duration = Date.now() - startTime;
console.log('函数调用成功:', {
callId,
duration: `${duration}ms`,
resultSize: JSON.stringify(result.result).length
});
return result.result;
} catch (error) {
const duration = Date.now() - startTime;
console.error('函数调用失败:', {
callId,
duration: `${duration}ms`,
error: error.message,
code: error.code
});
throw error;
}
};