记一次http请求超时的处理[转]

7,116 阅读4分钟

问题

项目的前端是vue,node端使用Express做中间层,后端告诉我们有个http请求需要耗时很久,说后续会优化,先应急处理(😅)。

在debug的过程中,发现一直出现timeout的情况,检查了客户端请求的设置,没有特殊限制请求的超时,然后想到会不会是服务端的问题,立刻翻查了Node的API
nodejs.org/dist/latest…

由文档中我们可以知道,Node服务器对每个客户端请求的默认连接时长是2min,在这个时间内,服务器没有发送响应信息,客户端的连接会被重置。

探讨

我们先创建个node服务器实例。

const http = require('http');
const server = http.createServer((req, res) => {  
  res.end();
});
server.listen(8000);

http.createServer创建一个HTTP服务器server, 在这个服务器中,我们添加了一个HTTP客户端请求监听函数,该函数包括两个参数:req和res。其中,req是一个客户端请求对象包含了客户端的请求信息,它是一个http.IncomingMessage实例。而res是服务器响应对象,它是一个http.ServerResponse实例,服务器处理完用户请求后通过访问对象向客户端返回响应信息

服务端的超时设置

对于一个http服务器来说,我们可能会设置以下两种类型的超时:

用户请求的超时 即上例中http.IncomingMessage实例-req

用户请求时间过长,会导致服务器停留在一个用户请求的接收状态,从而阻碍其他用户请求的进入。

服务器响应的超时 即上例中http.ServerResponse实例-res

服务器响应时间过长,会导致用户长时间接收不到服务器响应信息,影响用户体验和其他用户请求的处理

解决方案

Node.js HTTP服务器设置超时使用setTimeout()方法。

http.Server、http.IncomingMessage和http.ServerResponse中都有一个setTimeout()方法。server.setTimeout()会设置所有用户请求和服务器响应的超时时间,而req.setTimeout()只能针对本次请求超时时间进行设置,res.setTimeout()只能针对本次请求的服务器响应超时时间进行设置。我们可以像下面这样在HTTP服务端设置超时:

将所有的用户请求和服务器响应超时时间设置为5秒:

1
server.setTimeout(5000);

对于某一个比较耗时的用户请求, 我们将用户请求超时时间设置为20秒:

1
req.setTimeout(20*1000);

对于一个需要在服务器进行较长时间处理用户请求, 我们将服务器响应的超时时间设置为10秒:

1
res.setTimeout(10*1000);

使用Express.js中间件设置超时

Express.js提供了强大的中间件机制,利用这一机制我们可以更为灵活的设置HTTP服务器超时时间。

如,我们可以在app.js文件中添加如下处理程序,分别设置请求和服务器响应的超时时间,当然也可以只设置请求超时或服务器响应超时时间:

app.use(function(req, res, next) {  
  // 设置所有HTTP请求的超时时间  
  req.setTimeout(5000);  
  // 设置所有HTTP请求的服务器响应超时时间  
  res.setTimeout(5000); 
  next();
});

对比较某一个比较耗时的用户请求,或需要较多响应时间才能完成处理的请求,我们可以其路由处理方法或路由中间件中单独设置超时时间:

router.post('/file', function(req, res) {  
  req.setTimeout(20*1000)
});

使用Koa中间件设置超时,我们可以设置服务器响应的时间。

app.use(async (ctx, next) => {  
  ctx.res.setTimeout(0);  
  await next();
});

拓展

处理了服务端的请求响应设置后,问题还没有结束,如果前端项目部署有使用了nginx做proxy代理,那就需要在nginx处修改, 修改连接超时限制。

server {	 
/**/	 
  location / {		 
    proxy_connect_timeout 120s;		 
    proxy_read_timeout 120s;		 
    proxy_send_timeout 120s;	 
}}

简单说明下:

proxy_connect_timeout 默认值 60s

这个指令设置与upstream server的连接超时时间,如果你的upstream服务器起来了,但是hanging住了(例如,没有足够的线程处理请求,所以把你的请求放到请求池里稍后处理),那么这个声明是没有用的,由于与upstream服务器的连接已经建立了。

proxy_read_timeout 默认值 60s

该指令设置与代理服务器的读超时时间。它决定了nginx会等待多长时间来获得请求的响应。这个时间不是获得整个response的时间,而是两次reading操作的时间。

proxy_send_timeout 默认值 60s

这个指定设置了发送请求给upstream服务器的超时时间。超时设置不是为了整个发送期间,而是在两次write操作期间。如果超时后,upstream没有收到新的数据,nginx会关闭连接。

最后

Node的服务器在处理连接超时很灵活,这种响应时间较长的请求还是要尽量避免,尽管开了几个实例,当并发量一多,会占用很多网络资源。

转自:www.dazhuanlan.com/2020/04/28/…cf_chl_jschl_tk=e611af567dca04cf9d539db7b1c711726815020d-1599818733-0-AU4au0mzDey_Nfy0B48aqDwg1qbQsSU7GzIDrz3BA9UWW3mS2TR_Uf-xHtBd9CvDtpU_XeAAV7ZCAUU4TiEwVP77ykSDZpbm2_dQKsrmqaI6Wbf-akzDaA6WOuVniEjMyZ27niMdBuGhUuzG8NjYy9-2IY3GEIYOOCOT-C-Fc5vW6YI0Sn0ZaP2Jte6j91jwHscqdxmpZqnbb67k-FgUkvxsdnQsxosefRvRiAeYb06QYoVayY7PNnE2L9EYIspGrQ-X1z3BSamtrvEzEltXLlxOQrsgKo5WoF0tmkNPIo8ZT7cW594-POcQGOLQv5KeDw