背景
问题表现是无法访问域名,页面打不开,
使用culr命令同样的报错:
解决
原来使用nginx的ingress,迁移到higress后,前端访问higress-gateway
再调用后端服务,这个报错不是后端返回的,而是higress-gateway
。查看higress-gateway
的日志:
{...,"response_code":"502","response_flags":"UPE",...,"response_code_details":"upstream_reset_before_response_started{protocol_error}"}
搜索相关文档,找到一个:https://github.com/istio/istio/issues/24753 ,根据文档查看详细报错信息得修改日志级别为trace。修改higress-gateway
的yaml:
将warning修改为trace。pod重启后,再次访问查看trace日志:
从中可以看到:
completed header: key=transfer-encoding value=chunked
Error dispatching received data: http/1.1 protocol error: both 'Content-Length' and 'Transfer-Encoding' are set.
upstream reset: reset reason: protocol error, transport failure reason
upstream_reset_before_response_started{protocol_error}
encoding headers via codec (end_stream=false): ':status', '502'
说明这是前端的http1.1请求,使用了keep-alive,并且同时设置了Content-Length
和Transfer-Encoding: chunked
导致。
这样就有两种解决方法:
方法一
修改代码,Content-Length
和Transfer-Encoding: chunked
二者选其一。
方法二
使用EnvoyFilter
让envoy同时接受这两个header。参考:https://github.com/envoyproxy/envoy/issues/14004
vim envoyfilter.yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: envoy-filter
namespace: higress-system
spec:
configPatches:
- applyTo: CLUSTER
match:
context: ANY
patch:
operation: MERGE
value:
http_protocol_options:
allow_chunked_length: true
k apply -f envoyfilter.yaml
再次访问,恢复正常。
问题1
为什么从nginx切换到higress就无法访问了?
查看之前的ingress-nginx的yaml,pod使用的镜像版本:nginx-ingress-controller:v1.3.1
,进入容器查看nginx版本:
版本为1.19.10,是一个较低的版本。在高版本中nginx也遵循HTTP协议规范RFC。
问题2
为什么这两个header不能同时设置?
在HTTP协议中,keep-alive
、Content-Length
和Transfer-Encoding: chunked
传输编码涉及到数据传输的方式和连接的保持。
- Keep-Alive:是一种HTTP连接选项,用于保持连接在多个请求/响应对之间打开。启用keep-alive后,客户端和服务器可以复用同一个TCP连接,而不需要为每次请求都重新建立连接。
- Content-Length:是HTTP响应头字段,用来指示响应体的字节长度。客户端通过
Content-Length
知道何时读取到完整的响应体,这在保持连接复用的场景中尤其重要。 - Chunked Transfer-Encoding:当服务器无法在发送响应之前确定内容的长度时,使用
Transfer-Encoding: chunked
传输编码可以分块传输响应体。每个块都有自己的长度标识,客户端可以逐块接收数据,直到接收到长度为0的块,这标志着响应体的结束。
组合使用规则:
-
Content-Length
和Transfer-Encoding: chunked
不能同时使用:在同一个HTTP响应中,不能同时设置这两者。这是因为这两种方式都用来表示响应体的结束,若两者同时存在会造成混淆。 -
keep-alive可以与
Content-Length
或Transfer-Encoding: chunked
配合使用:- 当使用keep-alive时,需要确保客户端能够准确地知道响应体的边界,以便继续使用同一连接发送后续请求。
- 使用
Content-Length
时,客户端可以通过Content-Length
头知道响应体的结束位置,便于继续保持连接。 - 使用
Transfer-Encoding: chunked
编码时,客户端通过每个块的大小以及最后的零长度块来判断响应结束,支持keep-alive。
如何选择:
- 如果知道响应体的大小:使用
Content-Length
,这样可以在keep-aliv 模式下继续复用连接。 - 如果响应体大小不确定(例如,流式响应或动态生成的内容):使用
Transfer-Encoding: chunked
,这允许服务器边生成内容边传输,并仍然保持keep-alive连接。