重试Node.js http.request(发布,发布,删除)

时间:2018-09-03 17:46:47

标签: javascript node.js http

在nodejs中不使用任何第三方模块的情况下,实现对错误/条件重试的正确方法是什么?

我不确定如何针对错误调用相同的函数,以及如何将原始回调/数据传递给新调用的函数?

我需要销毁/结束插座吗?

我尝试查找示例,但只找到了似乎无效的第三方模块和http.get示例的引用。一个如何测试呢? 我尝试以下操作均未成功:

      async pingApi(cb) {
        let options = {
        "method":"post",
         "path": `/API/pingAPI?${this.auth}`, /ect do I reference this path?
          }
        };
        let request = await http.request(options, (response) => {
          let body = new Buffer(0);
          response.on('data', (chunk) => {
            body = Buffer.concat([body, chunk]);
          });
          response.on('end', function () {
            if (this.complete) {
              let decoded = new Buffer(body, 'base64').toString('utf8')
              let json = JSON.parse(decoded);
              if (json.result != 'OK') {
                setTimeout(pingApi, 1000); //cant pass callback
              } else {
                cb(null, json.result) //works
              }
            }
          });
        })
        request.end(); //does the socket need to be closed if retry is this function?
      }

向我指出正确的方向或批评的任何帮助将不胜感激,因为我认为这对我来说是非常重要的学习曲线。

先谢谢您

2 个答案:

答案 0 :(得分:2)

  

我不确定如何针对错误调用相同的函数,以及如何将原始回调/数据传递给新调用的函数?

我不确定您函数中的其他所有内容是否正确,但是您可以通过更改以下内容来解决所要求的递归:

setTimeout(pingApi, 1000); //cant pass callback

对此:

setTimeout(() => {
    this.pingApi(cb);
}, 1000);

这里没有显示整个上下文,但是如果pingApi()是一种方法,那么您还需要跟踪this的值,以便可以调用this.pingApi(db)。您可以使用以下箭头函数回调保留this的值:

response.on('end', () => { ... });

我注意到这里还有其他东西:

  1. 没有理由使用await http.request()http.request()不会返回承诺,因此将await与它一起使用并没有任何用处。
  2. 没有await,就没有理由将函数声明为async,因为没有人使用它的返回承诺。
  3. 目前尚不清楚if (this.complete)是做什么的。由于它位于常规函数回调中,因此this的值将不是您的pingApi对象。您通常应该将this保存在范围内的更高位置const self = this,或者内部需要将所有回调都用作箭头函数,以便保留this的值。
  4. 您可能应该将try/catch放在JSON.parse()周围,因为如果输入不是完美的JSON,它可能会抛出。
  5. 您可能不应永远重试。服务器真的讨厌永远重试的客户端,因为如果出现问题,客户端可能会无限期地每秒对服务器进行一次重击。我建议一定数量的最大重试次数,然后放弃一个错误。
  

我需要销毁/结束插座吗?

否,请求结束后会自动发生。

  

如何对此进行测试?

您必须在服务器中创建一个测试路由,该路由返回前几个请求的错误情况,然后返回成功的响应,并查看您的代码是否可以使用该路由。


这是尝试进行代码修复(未经测试):

const maxRetries = 10;

pingApi(cb, cnt = 0) {
    let options = {
    "method":"post",
     "path": `/API/pingAPI?${this.auth}`, // ect do I reference this path?
    };
    let request = http.request(options, (response) => {
      let body = new Buffer(0);
      response.on('data', (chunk) => {
        body = Buffer.concat([body, chunk]);
      });
      response.on('end', () => {
        if (this.complete) {
          let decoded = new Buffer(body, 'base64').toString('utf8')
          try {
            let json = JSON.parse(decoded);
            if (json.result != 'OK') {
                if (cnt < maxRetries)
                    setTimeout(() => {
                        this.pingApi(cb, ++cnt);
                    }, 1000);
                 } else {
                    cb(new Error("Exceeded maxRetries with error on pingApi()"));
                 }
            } else {
                cb(null, json.result) //works
            }
          } catch(e) {
              // illegal JSON encountered
              cb(e);
          }
        }
      });
    })
    request.end();
 }

有关此代码的尚待解决的问题:

  1. this.complete在做什么,this应该在引用什么?
  2. 为什么没有request.write()发送POST请求的正文?

我知道您不要求任何外部模块,但是我这样做的首选方法是使用Promise,并在http.request()周围使用请求-承诺包装器,因为它为您处理了很多此代码(检查为您提供response.status,为您解析JSON,使用Promise接口等)。您可以看到代码更简洁:

const rp = require('request-promise');
const maxRetries = 5;

pingApi(cnt = 0) {
    let options = {
        method: "post",
        url: `http://somedomain.com/API/pingAPI?${this.auth}`, 
        json: true
    };
    return rp(options).then(result => {
        if (result.result === "OK") {
            return result;
        } else {
            throw "try again";  // advance to .catch handler
        }
    }).catch(err => {
        if (cnt < maxRetries) {
            return pingApi(++cnt);
        } else {
            throw new Error("pingApi failed after maxRetries")
        }
    });
}

然后,使用示例:

pingApi().then(result => {
    console.log(result);
}).catch(err => {
    console.log(err);
})

答案 1 :(得分:1)

您对核心节点服务器使用async / await引起了我的兴趣,我已经尝试了尽可能多地使用这种新的异步功能。

这就是我的结局:https://runkit.com/marzelin/pified-ping

const pify = require("util").promisify;
const http = require("http");

const hostname = "jsonplaceholder.typicode.com";
const failEndpoint = "/todos/2";
const goodEndpoint = "/todos/4";

let options = {
  method: "get",
  path: `${failEndpoint}`,
  hostname
};


async function ping(tries = 0) {
  return new Promise((res) => {
    const req = http.request(options, async (response) => {
      let body = new Buffer(0);
      response.on("data", (chunk) => {
        body = Buffer.concat([body, chunk]);
      })

      const on = pify(response.on.bind(response));

      await on("end");
      let decoded = new Buffer(body, 'base64').toString('utf8')
      let json = JSON.parse(decoded);
      if (json.completed) {
        return res("all good");
      }
      if (tries < 3) {
        console.log(`retrying ${tries + 1} time`);
        return res(ping(tries + 1));
      }
      return res("failed");
    })

    req.on('error', (e) => {
      console.error(`problem with request: ${e.message}`);
    });

    // write data to request body
    req.end();
  })
}

const status = await ping();
"status: " + status