- 生产环境服务器规划如下
服务器 | 类型 | 网络环境 |
---|---|---|
cal.com | nginx | 外网 |
192.168.7.15:9200 | tomcat | 内网 |
192.168.7.16:9200 | tomcat | 内网 |
sdd.com | nginx | 内网 |
192.168.7.15:9100 | tomcat | 内网 |
192.168.7.16:9100 | tomcat | 内网 |
192.168.7.15和192.168.7.16是做个负载均衡。目前的需求是用户访问外网的cal.com 返回 内网 192.168.7.15:9200 和 192.168.7.16:9200的页面,数据需要再次请求sdd.com,sdd.com转发到内网的
192.168.7.15:9100 和 192.168.7.16:9100。
请求的流程图如图所示:
- 问题描述
在cal.com上线后,用户发起请求,发现9200端口上的静态页面请求可以返回,而ajax请求数据无法返回,所有的ajax请求是做了代理的,代理到了sdd.com。既然能返回静态页面数据,说明肯定不是cal.com对应的服务器问题,而是在sdd.com这一环节存在问题,通过添加日志输出,发现可以正常请求回来数据,并写入到响应体中去,但是前端一直请求错误。
- 解决
首先通过增加日志打印,排查方法的哪一步出现了问题,因为9200的tomcat服务器所有的静态资源是可以正常返回的,这排除了服务器问题,比较离奇的时候,所有的日志打印地方都可以正常输出,可以正常请求到sdd.com的结果,但是就是返回不了数据,这一步至少确定了 从9200发送sdd.com是没问题的。那么继续再排查其他原因,经过多次的代码反复注释,发现9200设在设置响应头的这一步出现了问题,具体的就是只要有Transfer-Encoding : chunked 就会导致浏览器报错。
经过分析此请求头发现,只要响应头中携带了这个Transfer-Encoding : chunked 标头,浏览器就会报错,只要一去掉,就不会报错。后来得知Transfer-Encoding 这是一种传输编码方式,如果存在这个标头,则表示服务器无法一次性计算出本次响应的content-length,需要将响应内容类型分段传输/分块传输给客户端,每一块都需要用一个十六进制的长度表示当前块传输内容的字节大小,当客户端收到最后一个块的大小为0时,表示此次请求完整的结束了。
错误代码:
clientHttpResponse.getHeaders().forEach((key, value) -> value.forEach(it -> {
response.setHeader(key, it);
}));
这里面的关键就是 response.setHeader(key, it); 给tomcat请求设置响应头 Transfer-Encoding :chunked ,但是实际的响应体数据格式却不是chunked 分块格式,这就导致了cal.com nginx服务器在接收到tomcat服务器的响应时根本无法处理成功,进而给浏览器也报错了。
这里由于sdd.com这台nginx服务器,开启了传入编码后
location / {
chunked_transfer_encoding on;
}
9200端口发送请求的工具类收到sdd.com返回的数据以后,进行了正确的格式解析,此时已经获取到了完整的响应体数据,那么这时候如果再按照sdd.com的响应头信息去给cal.com传输的话,就出现问题了,因为格式明明是完整的,而响应头却设置了 Transfer-Encoding : chunked 。这就导致匹配不起来了。