返回错误的时候该用哪个状态码?哪些是框架自己产生的?网关吐出 HTTP 状态时客户端怎么翻译?这篇把 17 个状态码逐个走一遍,再看几个容易踩的映射。
数字 名字 客户端看到 线上 grpc-status 0 OK OK 0 1 Canceled Canceled 1 2 Unknown Unknown 2 3 InvalidArgument InvalidArgument 3 4 DeadlineExceeded DeadlineExceeded 4 5 NotFound NotFound 5 6 AlreadyExists AlreadyExists 6 7 PermissionDenied PermissionDenied 7 8 ResourceExhausted ResourceExhausted 8 9 FailedPrecondition FailedPrecondition 9 10 Aborted Aborted 10 11 OutOfRange OutOfRange 11 12 Unimplemented Unimplemented 12 13 Internal Internal 13 14 Unavailable Unavailable 14 15 DataLoss DataLoss 15 16 Unauthenticated Unauthenticated 16
状态码在最后 trailers 的 grpc-status 里,是 0 到 16 的数字。实测服务端返回任何一个,客户端看到的名字与线上数字一一对应。
服务端返回 plain → 客户端 code=Unknown message="普通 error,不是 status" 服务端返回 ctxcancel → 客户端 code=Canceled message="context canceled" 服务端返回 ctxdeadline → 客户端 code=DeadlineExceeded message="context deadline exceeded" 服务端返回 wrapped → 客户端 code=NotFound message="包了一层: rpc error: code = NotFound desc = 内层 NotFound"
返回普通 error 一律变成 Unknown;返回 context.Canceled 或 context.DeadlineExceeded 会变成对应的 Canceled、DeadlineExceeded;用 %w 包装的 status 错误会被拆开,保留内层 code。想让调用方能区分错误,就返回 status.Error。
调用不存在的方法 → code=Unimplemented message="unknown method NoSuchMethod for service demo.Demo"
服务端要 2 秒、客户端 200 ms 超时 → code=DeadlineExceeded message="context deadline exceeded" 用时 201.1 ms
客户端 100 ms 后取消 → code=Canceled message="context canceled" 用时 101.1 ms
服务端不存在(端口没人监听),默认 fail-fast → code=Unavailable 用时 0.4 ms
message="connection error: desc = \"transport: Error while dialing: dial tcp 127.0.0.1:9802: connect: connection refused\""
同样但加 WaitForReady(true) → code=DeadlineExceeded 用时 801.1 ms
message="latest balancer error: connection error: desc = \"transport: Error while dialing: dial tcp 127.0.0.1:9802: connect: connection refused\""没有这个方法是 Unimplemented;超时是 DeadlineExceeded;客户端取消是 Canceled;连不上服务是 Unavailable。连不上默认立刻失败(0.4 ms),加 WaitForReady(true) 会等到截止时间再返回 DeadlineExceeded。
HTTP 状态 客户端 code message 200 Unknown "unexpected HTTP status code received from server: 200 (OK); transport: received unexpected content-type \"text/html\"\ndata: \"<html>200</html>\"" 400 Internal "unexpected HTTP status code received from server: 400 (Bad Request); transport: received unexpected content-type \"text/html\"\ndata: \"<html>400</html>\"" 401 Unauthenticated "unexpected HTTP status code received from server: 401 (Unauthorized); transport: received unexpected content-type \"text/html\"\ndata: \"<html>401</html>\"" 403 PermissionDenied "unexpected HTTP status code received from server: 403 (Forbidden); transport: received unexpected content-type \"text/html\"\ndata: \"<html>403</html>\"" 404 Unimplemented "unexpected HTTP status code received from server: 404 (Not Found); transport: received unexpected content-type \"text/html\"\ndata: \"<html>404</html>\"" 429 Unavailable "unexpected HTTP status code received from server: 429 (Too Many Requests); transport: received unexpected content-type \"text/html\"\ndata: \"<html>429</html>\"" 500 Unknown "unexpected HTTP status code received from server: 500 (Internal Server Error); transport: received unexpected content-type \"text/html\"\ndata: \"<html>500</html>\"" 502 Unavailable "unexpected HTTP status code received from server: 502 (Bad Gateway); transport: received unexpected content-type \"text/html\"\ndata: \"<html>502</html>\"" 503 Unavailable "unexpected HTTP status code received from server: 503 (Service Unavailable); transport: received unexpected content-type \"text/html\"\ndata: \"<html>503</html>\"" 504 Unavailable "unexpected HTTP status code received from server: 504 (Gateway Timeout); transport: received unexpected content-type \"text/html\"\ndata: \"<html>504</html>\""
如果路径上有一个不说 gRPC 的 HTTP/2 服务,客户端按 HTTP 状态映射:401 是 Unauthenticated、403 是 PermissionDenied、404 是 Unimplemented、429/502/503/504 是 Unavailable、400 是 Internal,而 500 和没有 grpc-status 的 200 都是 Unknown。网关的行为会直接影响客户端的重试判断(第 18 篇)。