Какие стандартные статусы ответов существуют в gRPC и как они работают?

Ответ

gRPC имеет собственный набор стандартных кодов состояния, которые более детальны, чем HTTP-статусы. Они определены в пакете google.golang.org/grpc/codes.

Ключевое отличие от REST API:
Даже если gRPC-вызов завершился с ошибкой (например, NotFound), HTTP-статус транспортного уровня чаще всего будет 200 OK. Сам gRPC-статус передается в трейлерах (trailers) HTTP/2 ответа, обычно в заголовках grpc-status и grpc-message.

Основные коды статусов:

  • OK (0): Запрос успешно выполнен.
  • Canceled (1): Операция была отменена клиентом.
  • InvalidArgument (3): Клиент указал неверный аргумент (например, ошибка валидации).
  • DeadlineExceeded (4): Время выполнения операции истекло до её завершения.
  • NotFound (5): Запрошенный ресурс не найден.
  • AlreadyExists (6): Попытка создать ресурс, который уже существует.
  • PermissionDenied (7): У клиента нет прав для выполнения этой операции.
  • Unauthenticated (16): Запрос не содержит валидных данных для аутентификации.
  • ResourceExhausted (8): Ресурс исчерпан (например, превышена квота).
  • Unavailable (14): Сервис временно недоступен. Клиенту рекомендуется повторить попытку позже.
  • Internal (13): Внутренняя ошибка сервера. Не следует раскрывать детали этой ошибки клиенту.
  • Unknown (2): Неизвестная ошибка.

Пример использования в Go:

Сервер (возврат стандартной ошибки):

import (
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
)

func (s *Server) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.User, error) {
    user, err := db.FindUser(req.Id)
    if err == sql.ErrNoRows {
        // Возвращаем стандартный статус NotFound
        return nil, status.Errorf(codes.NotFound, "user with id '%s' not found", req.Id)
    }
    // ...
}

Клиент (проверка статуса):

// ... после вызова RPC
if err != nil {
    st, ok := status.FromError(err)
    if ok {
        // Это gRPC ошибка
        if st.Code() == codes.NotFound {
            log.Println("Пользователь не найден, обрабатываем этот случай.")
        } else {
            log.Printf("gRPC ошибка: код=%s, сообщение=%s", st.Code(), st.Message())
        }
    } else {
        // Это не gRPC ошибка (например, проблема с сетью)
        log.Printf("Неизвестная ошибка: %v", err)
    }
}