membphis commented on code in PR #13224:
URL: https://github.com/apache/apisix/pull/13224#discussion_r3083439916
##########
apisix/core/response.lua:
##########
@@ -195,33 +194,33 @@ function _M.get_response_source(ctx)
return ctx._resp_source
end
- -- Priority 2: request was proxied — inspect the last
$upstream_header_time token
+ -- Priority 2: request was proxied — inspect $upstream_bytes_received
+ -- to determine if the upstream actually sent a response.
if ctx._apisix_proxied then
- -- Use pcall to safely access upstream_header_time via ctx.var, which
may
- -- error in certain NGINX error paths (e.g. connection refused).
- local header_time
+ -- $upstream_bytes_received: number of bytes received from upstream.
Review Comment:
# this way is wrong
here is the reason.
## 结论
`$upstream_header_time` 只有在 **Nginx 已经把本次 upstream 的完整响应头成功解析完** 时,才会是非 `-`。
源码上,`header_time` 在新一轮 upstream 尝试开始时先被初始化为 `-1`:
- `src/http/ngx_http_upstream.c:1580-1582`
只有在读取并解析响应头成功后,才会被赋值:
- `src/http/ngx_http_upstream.c:2594-2597`
日志变量输出时,`-1` 会被格式化成 `-`:
- `src/http/ngx_http_upstream.c:6047-6063`
所以判断标准很简单:
- 成功解析到完整响应头:非 `-`
- 在此之前任何失败:`-`
## 各场景说明
| 场景 | `$upstream_header_time` | 说明 |
| --- | --- | --- |
| 上游建立连接失败 | `-` | 还没进入“成功拿到完整响应头”的状态,`header_time` 保持 `-1`。 |
| 连接被拒绝(`ECONNREFUSED`) | `-` | 属于连接失败的一种。即使 `connect_time`
可能已有值,`header_time` 仍不会赋值。 |
| 连接阶段超时 | `-` | 超时发生在收响应头之前。 |
| 请求已发出,但一直没收到任何响应数据,读响应头超时 | `-` | `ngx_http_upstream_process_header()`
在超时分支直接走失败路径,没有给 `header_time` 赋值。参见 `src/http/ngx_http_upstream.c:2453-2455`。 |
| 收到了一部分响应头,然后连接关闭 | `-` | 只要还没把完整响应头解析成功,连接提前关闭仍然算失败。参见
`src/http/ngx_http_upstream.c:2523-2529`。 |
| 收到了一部分响应头,然后超时 | `-` | 同上,解析结果还没到 `NGX_OK`,所以仍是 `-`。 |
| 收到部分数据后已经可以判定响应头非法 | `-` | 例如状态行或某个头字段语法非法,`process_header()` 返回
`NGX_HTTP_UPSTREAM_INVALID_HEADER`,随后走失败路径。参见
`src/http/ngx_http_upstream.c:2583-2585`。 |
| 响应头过大(buffer 装不下,尚未成功解析) | `-` | 属于 `INVALID_HEADER` 失败路径。参见
`src/http/ngx_http_upstream.c:2558-2566`。 |
| 已成功收到并解析完整响应头,但没有响应体(例如 `Content-Length: 0`、`204`、`304`、`HEAD`) | 非 `-` |
`header_time` 在“头完整”这一刻就已经记下,是否有 body 不影响它。 |
| 已成功收到完整响应头,但后续一直没有响应体 / body 传输超时 / body 中途断开 | 非 `-` | `header_time`
已在头解析成功时落值,后续 body 阶段失败不会把它改回 `-`。 |
| 已成功收到完整响应头,随后 Nginx 因状态码走 `next_upstream` 重试 | 当前这次尝试是非 `-` | 因为重试判定发生在
`header_time` 赋值之后。参见 `src/http/ngx_http_upstream.c:2596-2605`。 |
## 关键边界
### 1. “收了部分应答头(非法头)”分两类
1. 只是“收了一部分”,但 Nginx 还没来得及判断是否合法,此时如果连接关闭或超时,结果是 `-`。
2. 已经收到足够多的数据,Nginx 可以明确判定为非法头,此时仍然是 `-`,因为 `header_time` 只在 `rc == NGX_OK`
时赋值,不会在 `INVALID_HEADER` 时赋值。
### 2. “有完整应答头但没有应答体”会是非 `-`
这是最容易误判的点。
`$upstream_header_time` 只关心“从开始连 upstream
到完整响应头解析成功”这段时间,不要求必须读到任何响应体。只要头已经完整,变量就已经有值了。
### 3. 错误状态码不影响是否落值
即使上游返回 `4xx/5xx`,只要响应头是完整且可解析的,`header_time` 仍然会先被写入,然后 Nginx 才决定是否拦截错误页或重试。
## 与 `$upstream_connect_time` / `$upstream_response_time` 的区别
- `$upstream_connect_time`:连接建立后、开始发请求时就可能落值,早于 `$upstream_header_time`。参见
`src/http/ngx_http_upstream.c:2159-2161`。
- `$upstream_header_time`:必须等到完整响应头解析成功。
- `$upstream_response_time`:请求结束(成功或失败)时补齐。参见
`src/http/ngx_http_upstream.c:4767-4768`。
所以会出现这类组合:
- 连接拒绝:`connect_time` 可能非 `-`,但 `header_time` 是 `-`
- 头成功、body 失败:`connect_time` 和 `header_time` 都非 `-`,`response_time` 也通常非 `-`
## 多次重试时的输出
如果启用了 `proxy_next_upstream` / `fastcgi_next_upstream` /
`uwsgi_next_upstream` / `scgi_next_upstream` 等重试机制,这个变量会为每次 upstream
尝试各记一项,并按顺序输出。
例如:
```text
- , 0.003 , 1.247
```
含义是:
- 第 1 次尝试在拿到完整响应头前就失败了
- 第 2 次尝试成功拿到头,耗时 3ms
- 第 3 次尝试成功拿到头,耗时 1.247s
对应格式化逻辑见:
- `src/http/ngx_http_upstream.c:6045-6088`
## 一句话版
`$upstream_header_time` 是否为非 `-`,只取决于这次 upstream
尝试有没有“成功解析出完整响应头”;连接失败、连接拒绝、连接/读头超时、只收了部分头、头非法,都会是 `-`;只要头完整,即使没有 body 或 body
后续失败,也会是非 `-`。
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]