【问题标题】:Express retriggering API endpoint on request timeouts在请求超时时快速重新触发 API 端点
【发布时间】:2022-01-12 01:02:05
【问题描述】:

背景:
我的 Express.js Web 服务器目前正在提供一个包装 SOAP 服务的 API(一些我无法更改的遗留服务)。 SOAP 服务需要处理动态数量的项目,处理每个请求大约需要 1.5 秒。 Nginx 服务器的超时时间为 60 秒。

问题:
对于此 API 的请求,例如假设需要超过 60 秒才能完成,我观察到该服务正在自动重新触发(我假设由 Express.js)。因此,如果在原始请求中我希望向表中插入 50 条记录,现在由于 API 的重新触发,我最终插入了 100 条记录(重复)。

这是显示问题的日志框架/示例:(敏感信息已被剥离)

January 10, 2022 15:35:44 [... ee905] - Starting myAwesomeAPI()  <-- Original API trigger
January 10, 2022 15:36:44 [... ff870] - Starting myAwesomeAPI()  <-- Re-trigger happens
January 10, 2022 15:36:54 [... ee905] - Completed myAwesomeAPI() <-- Original API ends (inserts 50 records in the table)
January 10, 2022 15:37:54 [... ff870] - Completed myAwesomeAPI() <-- Re-triggered API ends (inserting another 50 records in the table resulting in duplication)

我尝试过的:
重现问题并检查重新触发是否可以独立于 nginx。将 Nginx 超时设置为 60 秒,我将 Express 服务器的超时更改为 10 秒,并使用以下命令处理 15 个项目(在处理完成之前强制超时):

const express = require("express")
const server = express()
server.setTimeout(10000)  <-- sets all requests to have a 10 seconds timeout

// myAwesomeAPI code 

测试表明,10 秒后,超时“确实”重新触发了 API,并且 15 项重复(我看到插入了 30 条记录)。所以这告诉我 API 正在被 Express.js 重新触发。

问题:

  1. 如何阻止重新触发的发生,是否有快速服务器配置来启用/禁用超时自动重新触发?

解决方案和想法:

  1. 自从max items = 100(由团队设置)以来,将 Nginx 和 Express.js 超时增加到 300 秒应该是一个快速但肮脏的修复。我知道将异步 API 调用与某个近似时间联系起来纯属愚蠢(告诉我尝试向我团队中的其他工程师解释这一点;-p),所以我想避免这种方法。

  2. 创建具有某些列组合的复合键并对表实施插入限制。将此与检查复合键是否已插入/存在于表中并决定跳过/插入相结合。这种方法似乎更好一些。

  3. 另一种方法是在收到 API 调用后立即响应(这将关闭请求),然后继续处理请求。像这样的东西(灵感):https://www.bennadel.com/blog/3275-you-can-continue-to-process-an-express-js-request-after-the-client-response-has-been-sent.htm.

    这将使我独立于平台的超时设置,但会带走响应与不同项目状态的实时性,并增加通过其他查找等跟踪请求状态的复杂性。

【问题讨论】:

    标签: javascript node.js rest express nginx


    【解决方案1】:

    如果您能够更改前端,则可以向其添加事务 ID。将事务例程存储在与事务 ID 相关联的对象中,然后如果您收到正在进行的事务的 API 请求,则可以引用正在进行的事务。

    类似这样的:

    
    let transactions = {};
    
    router.get('/myapi', async (req,res,next) => {
      try {
        let {transactionID} = req.params;
        delete(req.params.transactionID);
        let transaction = transactions[transactionID];
        if(!transaction) {
          transaction = (async () => {
            let ret = await SOAPCall(req.params);
            // hold onto the transaction for some period of time
            let to = setTimeout(()=>{
              delete(transactions[transactionID]);
            }, 5000);
            to.detach(); // don't hold up process exit
            return ret;
          })();
          transactions[transactionID] = transaction;
        } 
        let ret = await transaction;
        res.json(ret);
      }
      catch(err) { next(err) }
    });
    
    

    【讨论】:

      猜你喜欢
      • 2016-02-06
      • 2019-09-10
      • 1970-01-01
      • 2019-08-13
      • 2013-12-07
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 2022-06-17
      相关资源
      最近更新 更多