问题现象
api访问时,如果访问过快,第二次访问大概率报Bad Request。
我们对网关做的操作
- 记录请求报文,打印日志。
- 对请求进行验签。
- 验签不通过,打回。验签通过,提取真正的请求参数。
- 根据请求报文和数据库配置,路由到后端的微服务,并附带真正的请求参数。
问题排查
- 排查网关日志,未发现异常现象。网关只打印了HTTP的报文体,未打印请求头。
- 排查微服务日志,发现tomcat接收的请求日志,HTTP请求头丢失了一部,所以Bad Request是微服务报出来的。
- 由此定位到网关出问题了。查看Netty的HttpServerCodec。发现请求头是一定会写的,为什么会丢失,怀凝是ByteBuf出问题了,导致一部分内容未写进去。
- 检查 ByteBuf并添加日志,确认ByteBuf没有问题。
- 由此判定是 tomcat解析出问题了,一部分请求头未解析到。
- 继续查看tomcat日志,无意中发现content-length与实际内容不一致。由此猜想,可能是对网关进行转发的时候,改变了网关的请求内容,而content-length是已经解析好的。所以需要重设content-length。
String content = toData.getString("content");
byte[] content4Byte = content.getBytes(StandardCharsets.UTF_8);
ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) {
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = HttpHeaders.writableHttpHeaders(super.getHeaders());
//如果有chunk代表是块,根据HTTP协议不需要contentLength字段
if(headers.getValuesAsList(HttpHeaders.TRANSFER_ENCODING).contains("chunked")){
return super.getHeaders();
}
//这里需要重新设置contentLength,因为转发的时候会改变原报文内容,导致内容长度计算不准确
headers.setContentLength(content4Byte.length);
return HttpHeaders.readOnlyHttpHeaders(HttpHeaders.readOnlyHttpHeaders(headers));
}
@Override
public URI getURI() {
Route route = new CachePostBodyGlobalFilter.Build().uri(URI.create(appMethod4Final.getHostUri())).id("a1").build();
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
log.info("reqId={},toUrl={}", reqId, appMethod4Final.getHostUri() + appMethod4Final.getReqUri());
return URI.create("http://127.0.0.1:8081/" + appMethod4Final.getReqUri());
}
@Override
public Flux<DataBuffer> getBody() {
//JSONObject toData = JSONObject.parseObject(body);
//String content = toData.getString("content");
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(content4Byte);
return Flux.just(bodyDataBuffer);
}
};
复盘
这次能解决问题,算是有点运气成分。不过在无法定位问题,而且源码比较复杂的情况下,是可以从日志中找到答案的。前提是要仔细看日志,并且日志足够。通过这次的问题,我必须要弄懂tomcat源码,要不然,下次再出问题,可能就没有这么幸运了。以下是要加深学习的地方:
- Http协议
- Https协议
- Http2
- netty
- tomcat
- reacotor
- spring-cloud-gateway
来源:oschina
链接:https://my.oschina.net/u/3217171/blog/4720770