bigywater.tech
gRPC · 3

gRPC 状态码:十几个 code 各自对应什么场景

返回错误的时候该用哪个状态码?哪些是框架自己产生的?网关吐出 HTTP 状态时客户端怎么翻译?这篇把 17 个状态码逐个走一遍,再看几个容易踩的映射。

17 个
gRPC 的状态码总数,编号 0 到 16,线上传的是数字
0.4 对 801.1 ms
连接被拒绝时,默认 fail-fast 与加 WaitForReady(true) 的返回时间
Unavailable
网关返回 429、502、503、504 时客户端得到的状态码(500 是 Unknown)

线上的状态码

实测 · 本机执行输出
  数字   名字                   客户端看到                线上 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 状态

实测 · 本机执行输出
  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 篇)。

分步演示

状态码
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Go 1.27.1 / grpc-go v1.84.0 / protobuf-go v1.36.12 / golang.org/x/net v0.57.0 / protoc 36.2(Homebrew);服务端和客户端都是本机起的 Go 进程(127.0.0.1,端口 9800 以上,不访问任何外部服务);需要看线上字节时用我自己写的 TCP 抓包代理加 golang.org/x/net/http2 的 Framer 解析 HTTP/2 帧;计时用 time.Now 或 perf_counter,数字每次运行会有小幅抖动。第四节的“不说 gRPC 的服务器”是 golang.org/x/net/http2/h2c 起的明文 HTTP/2 服务,每次返回指定的 HTTP 状态和一小段 HTML;所有结果是确定的,耗时是单次测量
    • 没有验证:Java 或 C++ 客户端的映射是否相同(这里只测了 grpc-go v1.84.0);服务端 panic 时的行为;错误详情 details 的传递(第 22 篇);状态码和重试策略的结合(第 18 篇);拦截器对状态码的改写;HTTP/1.1 网关(不支持 trailers)返回的情况
    京ICP备2026067502号-1京公网安备11010502063487号