假设有一种场景:
一个 http server
可以同时接受无数个请求,而每秒只能处理一个 request
,客户端设置的 timeout
为 30
秒(假设连接超时和读取超时的 timeout 阈值都是 30 秒)
此事客户端一次性发出了 1000
个请求,服务端都接受了这 1000
个请求,然后慢慢做,但是 30
秒只能做 30
个 request
的 response
,剩下的 970
个客户端直接等不及 timeout
(这个时候,既然 timeout 了,客户端如果不是 keepalive 应该直接四次挥手了吧?但是也可以不四次挥手吧)
等到第 31 秒的,server 端继续做剩下的 970 个的时候,发现 response 发过去没有客户端要了?!?!
这咋办?
这样有两个问题:
- server 端的资源被 970 个无效请求堵塞 970 秒,导致服务不可用
- server 消费无用的任务,造成执行成本浪费
显然我们要保证,这种情况不发生!
那业内的解决方案是什么呢?
我想到一种办法:
- 开始处理 requets 之前,都是用 http 协议或者更下面的 tcp 去问问客户端:喂,你(指客户端)还在不在听啊?
- 得到 ok 后在干活
- 得到 no ok 就直接抛弃 request
但是这种方式很 low,因为要网络 io,等于每个 request 之前都要走一次网络 io,开销太大了
nginx、apache httpd、tomcat、gunicorn 等等都是采用什么解决方案?
总不能也是在 start_request 之前都去问问客户端还在不在等 response 吧!
客户端 timeout 之后,一定会发起四次挥手吗?这个应该是不一定的吧?比如 keepalve 的长连接场景,客户端可能还行复用这个 http 连接呢(或者说 tcp 连接?我不确定 http 连接是不是等于 tcp 连接,多个 http 连接可以复用一个 tcp 连接吗?)
先放结论,浏览器端发起的请求都会被服务端继续处理,即使 js 代码中主动
abort
和timeout
。但是因为浏览器会有
max:6
的限制,所以如果请求没有发送出去的话,那么服务端不会继续处理。补充一下环境
客户端:浏览器 chrome
服务端:本地环境 node express
做了两个延时任务
占用 CPU 测试案例
可以看到,请求在前面并没有发出去,并且中间还有一个 TTFB 时间。
从服务端的响应看回来,只是当前 cpu 不占用时才会收到新的请求处理。
不占用 CPU 测试案例
可以看到,只是因为浏览器的并发限制,所有请求都会被服务端接收,只不过因为其中有异步处理,一直没收到回调而已。
测试中途断开连接(不占用 CPU 测试案例)
已发起,但是中途断开(第三个请求)
可以看到第三个请求前端是已经 cancel 了,并且第七个在取消的时候就已经正式的连通到了服务端。
在写的时候突然想起来,之前证明过一个类似的问题,是 chrome 请求数超过 6 的问题。
从服务端的日志截图可以看到,服务端在最后断开后仍然在处理
已发起,但是因为浏览器限制导致服务器没收到,客户端触发 cancel
从服务端截图可以看到,并不会收到请求。
demo 中的例子好像有点问题,因为是个微任务,所以有可能给了浏览器处理的时机,所以后面我贴了个同步终止的例子
已发起,但是因为服务端限制,导致服务器没有收到,客户端触发 cancel
可以看到,虽然只有前三个没取消,但是前六个还是像服务器发起请求了。

已发起,但是因为服务端限制,导致服务器没有收到,客户端触发 timeout
所有请求还是打到了服务端。