Node.js 16第二代GCF生成二维码时间歇性出现响应截断问题
第二代Google Cloud Function生成二维码出现截断响应问题
我有一个基于onRequest的第二代Google Cloud Function(GCF),运行在Node.js 16环境,使用node-qrcode包生成简单二维码。

第二代GCF具备高并发处理能力,但多数请求返回正常二维码的同时,约5%的请求返回黑方块,且GCF日志中出现如下提示:
Truncated response body. Usually implies that the request timed out or the application exited before the response was finished.
我已配置1个常驻实例、2GiB内存、60秒超时,生成该类简单二维码应无资源或超时问题。我怀疑存在Promise或await使用不当的问题,同时不清楚该截断提示的具体成因,GCF追踪未提供更多细节。注:二维码编码数据始终为整数格式。
GCF的qrCode.ts脚本
import { onRequest } from 'firebase-functions/v2/https' import { logger } from 'firebase-functions/v2' import QRCode from 'qrcode' import express from 'express' import cors from 'cors' import { kDebug } from './lib/constants' import { normalizedSku } from './lib/helperFunctions' const app = express() // 自动允许跨域请求 app.use(cors({ origin: true })) // 支持JSON编码和URL编码的请求体 app.use(express.urlencoded({ extended: true })) // 将二维码保存到GCF的临时内存并以文件形式返回 const filepath = '/tmp/qrcode.png' /** * 将二维码保存到文件。 * @param {string} qrCodePlainText 构成二维码的明文内容。 * @param {unknown} options 二维码生成包的配置选项。 * @return {boolean} 二维码是否成功保存到文件。 */ async function saveQrCodeToFile(qrCodePlainText: string, options: unknown): Promise<boolean> { try { await QRCode.toFile(filepath, qrCodePlainText, options) return true } catch (error) { logger.error(error) return false } } /** * 二维码图片生成器。GET请求中的值会被解码并作为生成二维码的明文内容,最终以PNG格式返回。 */ app.get('/qrCode/:data', async (req, res) => { let qrCodeData = req.params.data // 尺寸单位为英寸,例如值为1时生成1"x1"的二维码,最大值为10 const inchesParam = req.query.inches const returnToDataUrl = req.query.returnToDataUrl === 'true' const isSku = req.query.isSku === 'true' try { if (kDebug) { logger.info('请求参数', req.params) logger.info('查询参数', req.query) } if (!qrCodeData) { throw Error('缺少二维码数据参数') } if (!inchesParam) { throw Error('缺少尺寸(英寸)参数') } // 如果不是数字则返回NaN const inches = parseFloat(inchesParam as string) if (isNaN(inches)) { throw Error(`${inchesParam} 不是有效数字`) } if (kDebug) { logger.info(`二维码尺寸:${inches}"x${inches}"`) } if (inches > 10) { throw Error('尺寸参数不能大于10') } // 如果是SKU则标准化数据 if (isSku) { qrCodeData = normalizedSku(qrCodeData) } const options = { margin: 2, } // 将英寸转换为像素(1英寸=96像素) options['width'] = Math.round(inches * 96) if (kDebug) { logger.info(options) } const qrCodePlainText = decodeURIComponent(qrCodeData) if (returnToDataUrl) { res.send(await QRCode.toDataURL(qrCodePlainText)) return } else { if (!(await saveQrCodeToFile(qrCodePlainText, options))) { throw Error(`二维码生成失败,数据为:${qrCodeData}`) } res.sendFile(filepath) } } catch (e) { if (kDebug) { logger.error(e) } res.status(400).send(e.toString()) } }) exports.endpoint = onRequest( { timeoutSeconds: 60, region: ['us-east4'], memory: '2GiB', minInstances: 1 }, app, )
修改后的saveQrCodeToFile()函数(使用回调)
/** * 将二维码保存到文件。 * @param {string} qrCodePlainText 构成二维码的明文内容。 * @param {unknown} options 二维码生成包的配置选项。 * @return {boolean} 二维码是否成功保存到文件。 */ async function saveQrCodeToFile(qrCodePlainText: string, options: unknown): Promise<boolean> { try { return new Promise((resolve, reject) => { QRCode.toFile(filepath, qrCodePlainText, options, error => { if (error) { reject(error) } resolve(true) }) }) } catch (error) { logger.error(error) return false } }
内容的提问来源于stack exchange,提问作者Zelf
相关产品推荐
相关产品推荐

