跳到主要内容

在云函数中调用其他云函数

在云函数开发中,您可能需要在一个云函数中调用另一个云函数,以实现功能模块化、代码复用和业务逻辑分离。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 是在云函数中调用其他云函数的推荐方式,提供了简洁的 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
};
}
};

高级功能

版本控制调用

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;
}
};

相关文档