出现了两个案例:
- 接口返回400
- 接口返回414
先看第一个问题:状态码返回400
线上出现了几次接口400的异常,排查起来比较有意思,通过nginx代理过去就会400,通过ip直连过去,就不会出现,机智的我用tcpdump抓了一个包,搞下来看了一下,居然没有发现两种请求有什么不同(当然是我没发现)
我猜可能是刚好触发了tomcat的header最大大小的限制了,放到我自己编译的tomcat中开启debug模式,果然出现了异常
看到是Request header is too large异常,那到底是多大?
|
大小跟headerBufferSize有关,这个值最终是由决定
AbstractHttp11Protocol.java |
可以看到是8192
这个值可以通过tomcat的配置文件进行修改,因为我觉得没什么修改的必要,已经够大了,就不放出来了。
那回到上面的问题,为什么经过了nginx就好出问题呢?
仔细看了wireshark的包,发现了真相,原理nginx会增加一个X-Forwarded-Url
的header,会重复一遍url,我们本来的header在7k左右,然后有了这个header,瞬间double到了14k,超过了tomcat的header最大大小,被tomcat直接拒绝了。
如果你还不会从源码调试tomcat,那么这篇文章就对你有帮助啦
下面这个链接是可以直接用Intellj打开并进行debug的,基于tomcat8.0,做了一些细微的修改以便编译可以通过。
https://github.com/arthur-zhang/tomcat-code-reading
续上
近期发现 url 长度达到 2500 就跪了,跟我们之前看到的理论上支持 4000 左右(8192/2)相差比较远,抓包研究了一下,发现 Nginx 又丧心病狂的把 url 重复了三遍
导致最多支持的url长度大概为 2700 以下,本例中是 2500,因为要算上其它的 header
第二个问题,返回414状态码
其实这个很好理解,一个请求到后端接口内部,经历了两层组件 Nginx
和 Tomcat
容器
上面那个是 Tomcat
限制了能处理的最大header大小,经过了一层 Nginx 导致 header 大了三倍左右,导致一个能经过 Nginx 的请求,在 Tomcat 层被拒绝掉,返回400
状态码
那么Nginx层大小是怎么样的呢?
默认值也是8k
Syntax: large_client_header_buffers number size; |
写一个代码从url长度8192往下找到第一个非414的请求,发现不太对
为啥是8295
?而不是8k(8192)
仔细看一下文档A request line cannot exceed the size of one buffer, or the 414 (Request-URI Too Large) error is returned to the client.
意思是request line
不能超过一个buffer的大小(8k),否则会抛出 414
异常
什么是request line?http
的RFC上
有写Request-Line = Method SP Request-URI SP HTTP-Version CRLF
也就是只包含 method + uri + http-version CRLF
我们把对应的部分拷贝出来,看下长度,不包含CRLF的长度是8190。
我们可以大致计算一下GET请求的URI在nginx上最大不能超过多少:8192 - (GET ).length - ( HTTP/1.1).length - (CRLF).length = 8192 - 4 - 9 - 2 = 8177
跟我们用代码试出来的大小一样