← Назад к вопросам

Как gRPC работает с кастомными ошибками?

2.0 Middle🔥 151 комментариев
#Микросервисы и архитектура#Сетевые протоколы и API

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Обработка кастомных ошибок в gRPC

gRPC использует строго типизированную систему ошибок, основанную на кодах статусов из пакета google.golang.org/grpc/codes. Однако для передачи дополнительной структурированной информации вместе с ошибками существует механизм кастомных (пользовательских) ошибок, который реализуется через Status Details.

Основной механизм: Status с Details

Ключевая идея заключается в том, что любой gRPC-ответ содержит не только код ошибки и сообщение, но и опциональное поле details, которое может содержать сериализованные protobuf-сообщения любой структуры:

// Пример определения кастомной ошибки в .proto файле
syntax = "proto3";

package example;

import "google/rpc/error_details.proto";
import "google/rpc/status.proto";

message CustomErrorDetail {
  string error_code = 1;
  string domain = 2;
  map<string, string> metadata = 3;
  int64 timestamp = 4;
}

Практическая реализация в Go

1. Создание кастомной ошибки на сервере

import (
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
    "google.golang.org/protobuf/types/known/anypb"
)

func (s *Server) MyMethod(ctx context.Context, req *MyRequest) (*MyResponse, error) {
    // Бизнес-логика, которая может вернуть ошибку
    if err := validateRequest(req); err != nil {
        // Создаем кастомную деталь ошибки
        customErr := &CustomErrorDetail{
            ErrorCode: "VALIDATION_FAILED",
            Domain:    "auth",
            Metadata:  map[string]string{"field": "email"},
            Timestamp: time.Now().Unix(),
        }
        
        // Преобразуем в Any
        anyDetail, _ := anypb.New(customErr)
        
        // Создаем статус с деталями
        st := status.New(codes.InvalidArgument, "Invalid request parameters")
        st, _ = st.WithDetails(anyDetail)
        
        return nil, st.Err()
    }
    
    // Можно добавлять несколько деталей
    if isRateLimited(req) {
        err1 := &CustomErrorDetail{ErrorCode: "RATE_LIMIT"}
        err2 := &CustomErrorDetail{ErrorCode: "SUGGEST_RETRY_AFTER"}
        
        any1, _ := anypb.New(err1)
        any2, _ := anypb.New(err2)
        
        st := status.New(codes.ResourceExhausted, "Too many requests")
        st, _ = st.WithDetails(any1, any2)
        
        return nil, st.Err()
    }
    
    return &MyResponse{Success: true}, nil
}

2. Обработка кастомных ошибок на клиенте

import (
    "google.golang.org/grpc/status"
    "google.golang.org/protobuf/proto"
)

func callGRPCMethod() error {
    resp, err := client.MyMethod(ctx, &MyRequest{})
    if err != nil {
        // Получаем статус из ошибки
        st, ok := status.FromError(err)
        if !ok {
            // Это не gRPC ошибка
            return err
        }
        
        // Извлекаем детали
        for _, detail := range st.Details() {
            switch d := detail.(type) {
            case *CustomErrorDetail:
                fmt.Printf("Custom error: Code=%s, Domain=%s\n", 
                    d.ErrorCode, d.Domain)
                // Обрабатываем структурированную информацию
                if d.Metadata != nil {
                    for k, v := range d.Metadata {
                        fmt.Printf("  %s: %s\n", k, v)
                    }
                }
            case *errdetails.BadRequest:
                // Можно использовать стандартные детали Google
                for _, violation := range d.GetFieldViolations() {
                    fmt.Printf("Field violation: %s - %s\n",
                        violation.GetField(), 
                        violation.GetDescription())
                }
            }
        }
        
        return fmt.Errorf("gRPC error: %v", st.Message())
    }
    
    // Обработка успешного ответа
    return nil
}

Стандартные типы ошибок от Google

gRPC рекомендует использовать стандартные типы из пакета google.golang.org/genproto/googleapis/rpc/errdetails:

import "google.golang.org/genproto/googleapis/rpc/errdetails"

// Пример использования стандартных деталей
func createStandardError() error {
    st := status.New(codes.FailedPrecondition, "Precondition check failed")
    
    // Добавляем информацию о поле
    badRequest := &errdetails.BadRequest{
        FieldViolations: []*errdetails.BadRequest_FieldViolation{
            {Field: "email", Description: "Invalid email format"},
        },
    }
    
    // Добавляем информацию для отладки
    debugInfo := &errdetails.DebugInfo{
        Detail: "Stack trace or debug details",
        StackEntries: []string{"func1:line10", "func2:line25"},
    }
    
    st, _ = st.WithDetails(badRequest, debugInfo)
    return st.Err()
}

Best Practices и важные аспекты

  1. Согласованность схемы: Прототипы кастомных ошибок должны быть согласованы между клиентом и сервером через общие .proto файлы.

  2. Серийная совместимость: При изменении структуры CustomErrorDetail соблюдайте правила обратной совместимости protobuf.

  3. Лимитирование размера: Помните, что детали ошибок передаются в метаданных, которые имеют ограничения по размеру (обычно 8-16KB).

  4. Безопасность: Не включайте чувствительную информацию (пароли, токены) в детали ошибок.

  5. Множественные детали: Можно передавать несколько деталей разных типов в одной ошибке для разносторонней информации.

  6. Интернационализация: Для мультиязычных систем используйте коды ошибок вместо человекочитаемых сообщений в деталях.

Альтернативные подходы

// 1. Использование богатых ошибок (более сложный подход)
type RichError struct {
    Code     string
    Message  string
    Details  map[string]interface{}
    GRPCCode codes.Code
}

// 2. Встраивание информации в основное сообщение (простой способ)
func simpleCustomError() error {
    return status.Errorf(codes.NotFound, 
        "resource not found. ErrorID: %s, Context: %s", 
        generateErrorID(), getRequestContext())
}

Кастомные ошибки в gRPC предоставляют мощный механизм для структурированной передачи информации об ошибках между микросервисами, что особенно важно в распределенных системах, где стандартных кодов статусов часто недостаточно для полноценной диагностики проблем.

Как gRPC работает с кастомными ошибками? | PrepBro