部署Firebase Functions后Google Drive文件回调的重复ID与两类网络错误求助
嘿,我来帮你排查这两个头疼的问题,咱们一个个来拆解分析:
一、重复收到文件ID的问题
看了你的代码,我发现几个核心原因:
全局变量
urlArray的无状态干扰
Firebase Functions是无状态的函数服务,实例可能会被复用,全局的urlArray不会在每次触发时自动重置。如果实例被重复调用,旧的fileId还留在数组里,新的变更处理时可能会出现判断逻辑失效,或者如果重复拉取同一批变更,就会重复输出ID。fetchChanges循环逻辑的错误
你在fetchChanges里初始化pageToken = savedStartPageToken后,每次循环调用drive.changes.list时用的还是原始的savedStartPageToken,而不是更新后的pageToken!这会导致每次循环都拉取同一批变更,自然会重复输出fileId。Webhook中的实时监听触发循环
在/webhooks/drive的POST处理里,你用了onValue实时监听数据库的/drive/token变化,而fetchChanges里又会更新这个token,这就形成了一个死循环:webhook触发→读取token→调用fetchChanges→更新token→onValue再次触发→重复处理同一批变更。
修复建议:
- 把
urlArray改成fetchChanges内的局部变量,每次调用时初始化,避免全局数据污染: - 修正循环逻辑,用当前的
pageToken发起请求,而不是原始的savedStartPageToken: - 把Webhook里的
onValue改成一次性读取的get,避免循环触发:
修改后的关键代码片段:
async function fetchChanges(authClient, savedStartPageToken) { const drive = google.drive({version: 'v3', auth: authClient}); try { let pageToken = savedStartPageToken; // 局部数组,每次调用重置 const urlArray = []; do { const res = await drive.changes.list({ pageToken: pageToken, // 使用当前pageToken而非原始值 includeRemoved: false, spaces: 'drive', fields: '*', pageSize: 50 // 限制单次拉取数量,避免超时 }); res.data.changes.forEach((change) => { if (change.file.parents && change.file.parents[0] === folderId && !change.file.trashed && !urlArray.includes(change.fileId)) { urlArray.push(change.fileId); console.log(change.fileId) } }); pageToken = res.data.newStartPageToken; // 确保Firebase App只初始化一次 if (!getApps().length) { initializeApp(firebaseConfig); } const database = getDatabase(); await update(ref(database, "/drive"), { token: pageToken }); } while (pageToken); return pageToken; } catch (err) { throw err; } } // Webhook POST处理修改 app.post('/webhooks/drive', async function (req, res) { res.status(200).send('OK'); // 初始化Firebase App的统一判断 if (!getApps().length) { initializeApp(firebaseConfig); } const database = getDatabase(); const driveRef = ref(database, "/drive/token"); // 一次性读取token,而非实时监听 const snapshot = await get(driveRef); const driveToken = snapshot.val(); await authorize() .then((authClient) => fetchChanges(authClient, driveToken)) .catch(console.error); });
二、部署后出现的网络错误
这两个错误(socket hang up和TLS连接断开)主要和Firebase Functions的执行特性、资源初始化有关:
重复初始化Firebase App
你的代码在fetchChanges、watchChanges、Webhook处理里都调用了initializeApp(firebaseConfig),多次初始化App会引发内部资源冲突,干扰网络请求的正常建立。函数执行时间过长
如果fetchChanges的循环拉取大量变更,很容易超过Firebase Functions的执行时间限制(免费版默认90秒),函数被强制终止后就会出现socket hang up的错误。网络请求超时未设置
Google API的请求如果没有设置超时,遇到网络波动时可能会一直等待,最终触发连接断开的错误。
修复建议:
- 所有需要使用Firebase的地方,统一添加App初始化判断,确保只初始化一次(上面的代码已经包含);
- 给
drive.changes.list添加pageSize参数,限制单次拉取的变更数量,避免函数超时; - 给Google Drive客户端设置超时时间,避免请求无限等待:
const drive = google.drive({ version: 'v3', auth: authClient, timeout: 30000 // 设置30秒超时 });
备注:内容来源于stack exchange,提问作者leojail

