在执行给定请求时将 Express 置于维护模式
Put Express in maintenance mode while executing a given request
在我的应用程序中,我需要每晚同步一些资源。同步是通过将 PUT 请求发送到 /api/sync/
并在正文中包含 JSON 对象数组来启动的。我的任务是在我的应用程序中镜像 JSON 中的数据,所以首先我必须解决需要添加、更新和删除的内容,然后向数据库发出适当的查询。这可能需要一段时间,所以我不喜欢任何其他妨碍的请求。
所以我需要关闭 Express 的连接,进行同步并重新开始收听。但是有两个问题:
http.Server.close()
阻止接收新连接但等待已执行完成
- 可能就在
PUT /api/sync/
之后,可能会在前一个请求调用 close()
之前收到另一个请求。因此,将接受 2 个传入 PUT /api/sync/
请求进行处理。
目前我是这样创建 Express 应用程序的:
// index.js
const app = express();
app.server = new Server(app);
app.server.start();
// Server.ts
export class Server {
public readonly server: http.Server;
private readonly close: () => Promise<http.Server>;
private readonly listen: (port: number) => Promise<http.Server>;
constructor(public readonly app: express.Application) {
this.server = http.createServer(app);
this.close = promisify<http.Server>(this.server.close.bind(this.server));
this.listen = promisify<http.Server>(this.server.listen.bind(this.server));
}
async start() {
console.log('START');
await this.listen(Number(process.env.PORT));
}
async stop() {
console.log('STOP');
if (!this.server.listening) {
throw new Error('Server is not listening');
}
await this.close();
}
}
// middleware
module.exports = fn => (req, res, next) => {
console.log('START MAINTENANCE');
req.app.server.stop()
.then(() => {
const endMaintenance = (err) => {
console.log('END MAINTENANCE');
req.app.server.start().then(() => next(err));
};
Promise.resolve(fn(req, res)).then(() => endMaintenance())
.catch(err => endMaintenance(err));
});
};
// route definition
routes.put(
'/',
exclusiveRun(
async (req: Request, res: Response) => {
await dataSync.synchronize(req.body);
res.sendStatus(204);
}
)
);
但是,当我连续向 /api/sync
端点发送 2 个请求时,Express 接受了它们,只有其中一个抛出 "Server is not listening" 错误。我在哪里犯错误?这种方法是否足以防止中断同步过程(或在同步进行时以其他方式造成麻烦)?
您可以将请求处理程序作为您的第一个中间件,而不是关闭服务器,它可以检测您的服务器是否正在进行维护(您有一些标志),如果是,它会立即失败并显示 5xx 状态代码所有传入的请求。
然后,当您的 /api/sync
请求到达时,您设置该标志(阻止任何更多请求)并在所有当前处理的请求完成后开始同步。或者,您可以将此逻辑与实际关闭服务器相结合,这样在服务器实际关闭之前进入的任何其他请求都会获得 5xx 响应。
在我的应用程序中,我需要每晚同步一些资源。同步是通过将 PUT 请求发送到 /api/sync/
并在正文中包含 JSON 对象数组来启动的。我的任务是在我的应用程序中镜像 JSON 中的数据,所以首先我必须解决需要添加、更新和删除的内容,然后向数据库发出适当的查询。这可能需要一段时间,所以我不喜欢任何其他妨碍的请求。
所以我需要关闭 Express 的连接,进行同步并重新开始收听。但是有两个问题:
http.Server.close()
阻止接收新连接但等待已执行完成- 可能就在
PUT /api/sync/
之后,可能会在前一个请求调用close()
之前收到另一个请求。因此,将接受 2 个传入PUT /api/sync/
请求进行处理。
目前我是这样创建 Express 应用程序的:
// index.js
const app = express();
app.server = new Server(app);
app.server.start();
// Server.ts
export class Server {
public readonly server: http.Server;
private readonly close: () => Promise<http.Server>;
private readonly listen: (port: number) => Promise<http.Server>;
constructor(public readonly app: express.Application) {
this.server = http.createServer(app);
this.close = promisify<http.Server>(this.server.close.bind(this.server));
this.listen = promisify<http.Server>(this.server.listen.bind(this.server));
}
async start() {
console.log('START');
await this.listen(Number(process.env.PORT));
}
async stop() {
console.log('STOP');
if (!this.server.listening) {
throw new Error('Server is not listening');
}
await this.close();
}
}
// middleware
module.exports = fn => (req, res, next) => {
console.log('START MAINTENANCE');
req.app.server.stop()
.then(() => {
const endMaintenance = (err) => {
console.log('END MAINTENANCE');
req.app.server.start().then(() => next(err));
};
Promise.resolve(fn(req, res)).then(() => endMaintenance())
.catch(err => endMaintenance(err));
});
};
// route definition
routes.put(
'/',
exclusiveRun(
async (req: Request, res: Response) => {
await dataSync.synchronize(req.body);
res.sendStatus(204);
}
)
);
但是,当我连续向 /api/sync
端点发送 2 个请求时,Express 接受了它们,只有其中一个抛出 "Server is not listening" 错误。我在哪里犯错误?这种方法是否足以防止中断同步过程(或在同步进行时以其他方式造成麻烦)?
您可以将请求处理程序作为您的第一个中间件,而不是关闭服务器,它可以检测您的服务器是否正在进行维护(您有一些标志),如果是,它会立即失败并显示 5xx 状态代码所有传入的请求。
然后,当您的 /api/sync
请求到达时,您设置该标志(阻止任何更多请求)并在所有当前处理的请求完成后开始同步。或者,您可以将此逻辑与实际关闭服务器相结合,这样在服务器实际关闭之前进入的任何其他请求都会获得 5xx 响应。