Google Pub/Sub本地正常,生产环境无法推送谷歌商家评论至Facebook
Google商家评论推送Facebook功能生产环境失效排查方案
我开发了一套接收Google商家评论通知的系统,已完成Google商家登录及权限配置,基于Node.js实现了Pub/Sub订阅消息拉取功能。本地运行时,星级≥4的评论能正常生成图片并推送至Facebook,但部署到生产环境后该功能失效。相关核心代码如下:
async function pubsubReview() { const starNum = (str) => { str = str.toUpperCase(); if (str == "ONE" || str == "STAR_RATING_UNSPECIFIED") { return 1; } else if (str == "TWO") return 2; else if (str == "THREE") return 3; else if (str == "FOUR") return 4; return 5; }; try { const [subs] = await pubSubClient.getSubscriptions(); console.log("Subscriptions:"); subs.forEach((s) => console.log(s.name)); const subscription = pubSubClient.subscription(subscriptionNameOrId); // Receive callbacks for new messages on the subscription subscription.on("message", async (message) => { await message.ackWithResponse(); console.log("Received message:", message.data.toString()); try { console.log("\n\n", JSON.parse(message.data.toString()), "\n\n"); const noti = JSON.parse(message.data.toString()); const accountName = noti.location.split("/locations")[0]; const reviewName = noti.review; const review_name_parts = reviewName.split("/"); const location_name = review_name_parts[2] + "/" + review_name_parts[3]; const query = ` SELECT c.company_id, st.page_token, st.selected_template_url, st.enable_bg, c.location_ids FROM (SELECT location_ids, company_id FROM google_my_business WHERE account_name = '${accountName}' ) c JOIN (SELECT * FROM socials_token ) st ON c.company_id = st.company_id `; const queryRes = await promiseQuery(query); const pageToken = queryRes[0].page_token; const companyId = queryRes[0].company_id; let enable_bg = queryRes[0].enable_bg; const locationsAddedCheckQuery = `SELECT locations_added FROM google_my_business WHERE company_id = ${companyId}`; const locationsAddedCheckQueryRes = await promiseQuery( locationsAddedCheckQuery ); let locationsAddedCheck = locationsAddedCheckQueryRes[0].locations_added; if (!locationsAddedCheck) { return res .status(200) .send({ data: "[]", success: true, toggle: false }); } let toggleCheck = `SELECT location_ids FROM google_my_business WHERE company_id = ${companyId}`; let toggleCheckRes = await promiseQuery(toggleCheck); toggleCheck = JSON.parse(toggleCheckRes[0].location_ids); toggleCheck = toggleCheck.filter((item) => { return item.toggle && item.name == location_name; }); console.log("\n\ntoggle Check is\n", toggleCheck); if (toggleCheck.length > 0) { console.log("\n\ntrying to make post to fb\n"); try { const reviewData = await fetchReview(reviewName, companyId); const revieweeName = reviewData.reviewer.displayName; const reviewText = reviewData.comment; const stars = starNum(reviewData.starRating); if (stars >= 4) { let templateUrl = queryRes[0].selected_template_url; console.log("\n", templateUrl, "\n", pageToken, "\n"); const response = await axios.get(templateUrl, { responseType: "arraybuffer", }); const bufferTemp = Buffer.from(response.data, "binary"); let image = await Jimp.read(bufferTemp); let buffer = null; if (enable_bg) { buffer = await generateReviewImageWithBG( image, stars, revieweeName, reviewText ); } else { buffer = await generateReviewImage( image, stars, revieweeName, reviewText ); } const publicUrl = await uploadToGCP(buffer, reviewName); console.log( "\n\nreview created successfully\n", publicUrl, "\n\n" ); const imgPost = await postImageToFB( pageToken, publicUrl, "demo-post" ); await deleteFromGCP(reviewName); console.log("\n\nposted review to FB and deleted from GCP"); } } catch (err) { console.log("\n\nfailed to make post with error\n", err, "\n\n"); throw err; // Rethrow the error to be caught in the listener's catch block } } else { console.log("\n\nwill not post to fb\n\n"); } } catch (err) { console.log("\nGot error While running PubSub Listener\n", err, "\n\n"); } }); // Receive callbacks for errors on the subscription subscription.on("error", (error) => { console.error("Received error:", error); }); } catch (err) { console.log("\n\n", err, "\n\n"); } } pubsubReview();
排查方向与解决方案
1. Pub/Sub消息确认时机错误
- 当前代码在收到消息后立即调用
await message.ackWithResponse();,但后续异步操作(数据库查询、图片生成、FB推送)尚未完成。生产环境中如果处理耗时较长,Pub/Sub会判定消息已处理完成,但实际流程失败后无法重新投递。 - 修复:将消息确认逻辑移到所有异步操作成功完成之后,确保只有全流程执行成功才确认消息。若中间失败,调用
message.nack()让Pub/Sub重新投递:subscription.on("message", async (message) => { try { console.log("Received message:", message.data.toString()); // 原有所有业务逻辑... // 全流程成功后再确认消息 await message.ackWithResponse(); console.log("\n\n流程完成,消息已确认"); } catch (err) { console.log("\n处理失败:\n", err); // 拒绝消息,触发重新投递 message.nack(); } });
2. 生产环境权限与网络限制
- GCP权限:检查生产环境服务账号是否拥有Pub/Sub订阅权限、GCS存储读写权限、Google My Business API访问权限。
- Facebook API访问:确认生产环境服务器能访问Facebook Graph API,Page Token是否有效(未过期、权限覆盖发布图片),且生产环境域名已在FB开发者平台配置。
- 模板图片访问:验证
templateUrl对应的地址在生产环境是否可访问,避免内网地址或权限限制导致图片拉取失败。
3. 数据库查询风险与数据一致性
- 当前使用字符串拼接SQL(
'${accountName}')存在SQL注入风险,若accountName含特殊字符会直接导致查询失败。同时生产环境数据可能与本地不一致(如page_token为空、location_name不匹配),导致流程终止。 - 修复:改用参数化查询避免注入;添加查询结果校验逻辑,比如判断
queryRes[0]是否存在、pageToken是否有效:const queryRes = await promiseQuery(query); if (!queryRes || queryRes.length === 0) { console.log("未找到匹配的企业数据"); message.nack(); return; } const pageToken = queryRes[0].page_token; if (!pageToken) { console.log("Page Token为空"); message.nack(); return; }
4. 错误处理与日志缺失
- 生产环境可能未配置完整的日志采集,导致无法定位具体失败环节(如
fetchReview、postImageToFB的错误未被记录)。 - 修复:配置生产环境日志系统(如GCP Cloud Logging),在每个异步步骤添加详细日志,记录关键数据(如
reviewData内容、FB接口响应)。
5. 资源限制问题
- 生产环境服务器内存不足可能导致Jimp图片生成时崩溃;异步操作过多也会阻塞事件循环。
- 排查:监控服务器内存、CPU使用率;优化图片生成逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Sehej wahla
相关产品推荐
相关产品推荐

