在执行给定请求时将Express置于维护模式

时间:2019-07-28 15:59:31

标签: express asynchronous maintenance-mode

在我的应用中,我需要每晚同步一些资源。通过使用主体中的JSON对象数组向/api/sync/发送PUT请求来开始同步。我的任务是在应用程序中镜像JSON中的数据,因此首先我必须解决需要添加,更新和删除的内容,然后向数据库发出适当的查询。可能要花一些时间,所以我不喜欢其他要求。

所以我需要关闭Express的连接,进行同步,然后再次开始监听。但是有两个问题:

  1. http.Server.close()阻止接收新连接,但等待已执行完毕
  2. 有可能紧接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接受它们,然后只有其中一个抛出“服务器未监听”错误。我在哪里出错?这种方法是否足以防止破坏同步过程(或在进行同步时造成麻烦)?

1 个答案:

答案 0 :(得分:1)

除了关闭服务器,您还可以将请求处理程序作为检测您的服务器是否正在执行维护的请求处理程序(您拥有一些标志),并且如果这样,它会立即失败,并显示5xx状态码要求。

然后,当您的/api/sync请求进入时,您设置该标志(阻止更多请求),并在所有当前正在处理的请求完成后开始同步。或者,您可以将此逻辑与实际关闭服务器结合起来,这样在服务器实际关闭之前出现的任何其他请求都将获得5xx响应。