线上故障-ERR_CONNECTION_CLOSED

例行周四的技术会议时,有人抛出了一个线上故障,Web上有个表格表头过滤,当选择所有值请求时会报错ERR_CONNECTION_CLOSED,但当选择较少的时正常返回200。因为不清楚ERR_CONNECTION_CLOSED的含义,因此排查过程也走了点弯路,这里总结下。

ERR_CONNECTION_CLOSED

Chrome下请求报错但没有状态码,因此一开始并不确定是服务端还是浏览器层面报错。

但一般还是从前端排查起。

request body limit?

现象上来看,参数值减少即返回200,参数值较多就会报错,因此一开始判断可能是请求body体的大小限制。

因此随便构造了较长的数组参数,进行请求发现后端正常返回信息,当然只是会报非法参数错误,因此推翻是body大小限制。

header length limit?

注意到报错时还有个特征是request header中会传递当前URL,而表格表头过滤操作时,URL也会更新,因此可能是header长度问题。

报错时,请求头大概有10KB,为了印证这一点,自定义一个header字段,填充值,使得长度达到10KB,同时选择一个之前正常返回200的值进行测试,发现还是报错了。

因此可以确定并非某个参数值在业务层面导致的报错,而是request header长度限制导致。

解决

原因一旦找到,解决办法也就明确

  1. 增加容器header长度限制

    • HTTP本身对于req.header长度并没限制,只是各个容器有限制
  2. 修改请求头,考虑避免传递过大请求头问题,比如这里是传递了较长的URL作为header的参数,只是为了作为日志记录,因此完全可以没必要传递完整URL参,因此这里最终决定采用截取方案,即限制最长URL,同时需要注意过长URL本身就不合理,因为浏览器对于URL也存在长度的限制

需要知道,这里是因为header长度限制报错导致,但如果body长度超过,也会出现该错。

回过头来,再去看报错信息ERR_CONNECTION_CLOSED,也就明白了

HTTP连接是建立在TCP连接之上,如上当header长度超过后,服务器直接关闭了该TCP链接,而平时所谓的500,400只是HTTP连接结束,但TCP连接还存在,两者还是有根本的不同。

既然是链接,因此请求一定是正常发送到了服务端,只是在服务端容器那层因为长度限制超过而报错了。

node/–max-http-header-size

问题解决了,为了强化理解,这里以node为例,来复现下问题

node对于header大小限制默认是8KB,于是这里增加较大的自定义header项,请求node服务端,发现请求报错。重启启动node,同时指定header上限为1500000,发现返回200。

1
2
3
4
5
// 启动服务端
node --max-http-header-size 1500000 app.js

// JS自定义头部字段信息
xhr.setRequestHeader('...')

总结

一般而言习惯根据Response Status来判断后端错误,但是比如上述报错并没有status,但确实是服务端报错。

因此需要有这样认知,服务端报错并不一定有responseStaus