背景
今天在生产环境碰到了一个不算复杂,但是容易让人抓狂的问题。我们的一个Rest接口偶发的报406错误,只在生产环境中出现,大致估算是三千次调用中会出现十几次的这个错误,在测试环境一直无法复现。 首先查了下HTTP的406状态码语意,如下:
406 (SC_NOT_ACCEPTABLE)表示请求资源的MIME类型与客户端中Accept头信息中指定的类型不一致。 例如: 客户端请求html类型资源,但是服务端返回了json数据,就回报406错误
我们的接口是REST 接口,请求的类型和返回的都是JSON 类型的 应该是不会有这个问题的,而且还是偶发的,一个月可能会出现几次,大致情况就是这样。
排查思路
- 首先,我先看了一下这个接口代码,我写了个类似的接口,path变量参数是IP,
@RestController
public class TestController {
@Resource
private TestService testService;
@PostMapping("/test/{ip}")
public String doReq( @PathVariable("ip")String ip){
String result = testService.doSomething(ip);
return result;
}
}
因为这块代码不是我写的,所以我刚看到这个代码的时候就感觉有点怪异,因为ip地址中是有特殊字符的,而且ipv6的地址中带有冒号,可能会存在歧义问题,咱们常规的接口开发中是不建议带特殊字符的。后来问题定位出来也验证了确实是这里的问题。 2. 然后,根据406的错误语意,我们猜测是在客户端发请求的时候指定的Accept请求头有问题,我们把Accept请求头打印出来之后,也都是正常的其中“/” 就代表接受所有类型的响应,如果是这里的问题所有请求都是有问题的,不会是偶发的,所以排除了请求头的怀疑。 3. 后来,我们又从报错的时间上开始找规律,但是报错时间毫无规律,就是偶发的,上午下午都有。 4. 再然后,考虑到请求URI上的后缀也会影响到服务端响应类型,例如我们请求 http://test.com/a.jpg 但是服务端返回一个json也是会报406,而我们的接口地址上是以IP结尾的,IPv4地址就回有点,猜测是不是这个影响,然后通过日志观察,果然报错的请求的ip地址都是 .123 结尾的,然后查询了tomcat 的mimetype , 真的是有一个类型是123 如下图:
结论
- 就是因为我们的接口地址以IP结尾,所以对于以123结尾的IP地址,会被tomcat误认为一种mimetype,从而导致返回的json数据会报406。
- 解决的方式就把这个接口参数调整一下,不要以IP作为结尾即可。
- 所以这也是为什么在测试环境无法复现,因为我们的测试都是在内网中,大家的ip都是固定的,以123结尾的ip特别少,所以一直没有暴露出来这个问题。
问题总结
- 开发过程中一定要注意特殊字符的影响,在定义变量啊,命名等方面特殊字符都会影响到我们代码逻辑,慎用特殊字符。
- 在看似偶然的事件背后,肯定是存在一些规律性,面对这种偶发的问题,要注意细节寻找规律,从时间上,空间上等方面下手。
|