【发布时间】:2022-02-14 21:13:17
【问题描述】:
我有一个在 Google App Engine 上运行的 NextJS Typescript 应用。它从 Firestore 获取数据,一切正常。为了提高应用程序的速度,我正在试验新的数据获取基础架构,其中服务器侦听 Firestore 集合,并在 Firestore 中进行更改时将所有数据更新到 tmp 文件夹中的 JSON 文件。这样,所有数据都是最新的,并且始终可供 App Engine 使用。在本地,这就像一个魅力。
有一些明显的事情我需要改进,但对我来说下一步是在 GCP 中运行一个开发项目,看看我的内存使用情况是否正常,以及它是否能像我希望的那样快速运行等等。但问题是当我更改 NextJS 基础结构以包含自定义服务器时,App Engine 和 Firestore 之间的连接消失了。
我在 GCP 日志中看到的问题是:
Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
at GoogleAuth.getApplicationDefaultAsync (/workspace/node_modules/google-auth-library/build/src/auth/googleauth.js:180:19)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at runNextTicks (node:internal/process/task_queues:65:3)
at listOnTimeout (node:internal/timers:526:9)
at processTimers (node:internal/timers:500:7)
at async GoogleAuth.getClient (/workspace/node_modules/google-auth-library/build/src/auth/googleauth.js:558:17)
at async GrpcClient._getCredentials (/workspace/node_modules/google-gax/build/src/grpc.js:145:24)
at async GrpcClient.createStub (/workspace/node_modules/google-gax/build/src/grpc.js:308:23)
客户端中的实际错误消息是“502 Bad Gateway – nginx”。
之前我有一个基本的 NextJS 应用程序,它有前端页面和后端 API 路由。路由连接到 Firestore 并将该数据提供给正确的用户等。主要区别在于我添加了一个自定义服务器来启动侦听器:
import { Firestore } from '@google-cloud/firestore';
import express, { Request, Response } from 'express';
import next from 'next';
import fs from 'fs';
import os from 'os';
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const port = process.env.PORT || 3000;
let firestoreListeners: { [collectionId: string]: () => void } = {};
const unsubscribeAllListeners = () => {
for (const collectionId of Object.keys(firestoreListeners)) {
console.log('unsubscribing from', collectionId);
firestoreListeners[collectionId]();
}
firestoreListeners = {};
};
const skippedCollections = ['analytics', 'pageRevisions', 'newsItemRevisions'];
app.prepare().then(() => {
const server = express();
unsubscribeAllListeners();
const firestoreSettings = {} as FirebaseFirestore.Settings;
if (process.env.GCP_KEYFILE_NAME) {
firestoreSettings.keyFilename = process.env.GCP_KEYFILE_NAME;
}
const firestoreData: {
[collectionId: string]: {
[id: string]: any;
};
} = {};
const firestore = new Firestore(firestoreSettings);
firestore.listCollections().then((collections) => {
for (const collection of collections) {
if (
!firestoreListeners[collection.id] &&
!skippedCollections.includes(collection.id)
) {
console.log('listening to', collection.id);
firestoreData[collection.id] = {};
const listener = firestore
.collection(collection.id)
.onSnapshot((snapshot) => {
firestoreData[collection.id] = {};
for (const doc of snapshot.docs) {
firestoreData[collection.id][doc.id] = {
_id: doc.id,
...doc.data(),
};
}
if (!fs.existsSync(os.tmpdir() + '/data')) {
fs.mkdirSync(os.tmpdir() + '/data');
}
fs.writeFileSync(
os.tmpdir() + `/data/${collection.id}.json`,
JSON.stringify(firestoreData[collection.id])
);
console.log(
'updated',
collection.id,
'with',
snapshot.docs.length,
'docs'
);
});
firestoreListeners[collection.id] = listener;
}
}
});
server.all('*', (req: Request, res: Response) => {
return handle(req, res);
});
server.listen(port, (err?: any) => {
if (err) throw err;
console.log(
`> Ready on localhost:${port} - env ${process.env.NODE_ENV}`
);
});
server.on('close', function () {
unsubscribeAllListeners();
});
process.on('beforeExit', () => {
unsubscribeAllListeners();
});
});
构建和部署脚本没问题,如果我将侦听器逻辑排除在外,只部署自定义服务器,它就可以工作。
有什么问题?是 nginx 的问题还是我有其他问题?
【问题讨论】:
-
如果我将监听器逻辑移动到 /_ah/warmup 路由,它会工作,然后每个路由都会启动,但是很难确保每个实例只有一个监听器。但关键可能是它与 NextJS API 路由中的侦听器逻辑一起使用,但当我使用服务器完成侦听器逻辑时则不行。
-
请发表您的评论作为答案,以便社区发现这很有帮助。
-
我不觉得我的评论真的是一个答案。
标签: node.js google-app-engine google-cloud-platform google-cloud-firestore next.js