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

Что делать при возникновении ошибки Internal из сервиса?

3.0 Senior🔥 191 комментариев
#Observability

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

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

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

Стратегия реагирования на Internal ошибки от сервиса

При возникновении Internal Server Error (ошибки 5xx, особенно 500) от стороннего или внутреннего сервиса, подход должен быть комплексным и многоуровневым. Вот пошаговая стратегия, которую я применяю в проектах на Go.

1. Немедленная реакция и обеспечение устойчивости

Первое правило — не допустить каскадных сбоев в вашей системе.

// Пример реализации повторных попыток (retry) с экспоненциальной задержкой
func callExternalServiceWithRetry(ctx context.Context, maxRetries int) (*Response, error) {
    var lastErr error
    
    for i := 0; i < maxRetries; i++ {
        resp, err := externalService.Call(ctx)
        if err == nil {
            return resp, nil
        }
        
        // Проверяем, стоит ли повторять запрос
        if !shouldRetry(err) {
            return nil, err
        }
        
        lastErr = err
        
        // Экспоненциальная задержка
        delay := time.Duration(math.Pow(2, float64(i))) * time.Second
        select {
        case <-time.After(delay):
            continue
        case <-ctx.Done():
            return nil, ctx.Err()
        }
    }
    
    return nil, fmt.Errorf("max retries exceeded, last error: %w", lastErr)
}

func shouldRetry(err error) bool {
    // Повторяем только для временных ошибок
    if err == nil {
        return false
    }
    
    // Проверяем тип ошибки
    var apiErr *APIError
    if errors.As(err, &apiErr) {
        return apiErr.StatusCode >= 500 // Повторяем для всех 5xx ошибок
    }
    
    // Проверяем таймауты и сетевые ошибки
    if os.IsTimeout(err) || isNetworkError(err) {
        return true
    }
    
    return false
}

2. Внедрение механизмов защиты

Circuit Breaker — критически важный паттерн для изоляции проблемных сервисов:

// Использование готовой библиотеки gobreaker
import "github.com/sony/gobreaker"

var cb *gobreaker.CircuitBreaker

func init() {
    settings := gobreaker.Settings{
        Name:        "ExternalService",
        MaxRequests: 5,
        Interval:    30 * time.Second,
        Timeout:     60 * time.Second,
        ReadyToTrip: func(counts gobreaker.Counts) bool {
            // Размыкаем цепь если 60% запросов завершаются ошибкой
            failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
            return counts.Requests >= 10 && failureRatio >= 0.6
        },
    }
    cb = gobreaker.NewCircuitBreaker(settings)
}

func callProtectedService(ctx context.Context) (interface{}, error) {
    return cb.Execute(func() (interface{}, error) {
        return externalService.Call(ctx)
    })
}

3. Мониторинг и логирование

Необходимо детально логировать контекст ошибки:

// Структурированное логирование ошибок
type RequestContext struct {
    RequestID  string
    Service    string
    Endpoint   string
    Timestamp  time.Time
    Duration   time.Duration
    StatusCode int
}

func logServiceError(ctx context.Context, reqCtx RequestContext, err error) {
    logger.WithFields(logrus.Fields{
        "request_id":   reqCtx.RequestID,
        "service":      reqCtx.Service,
        "endpoint":     reqCtx.Endpoint,
        "duration_ms":  reqCtx.Duration.Milliseconds(),
        "status_code":  reqCtx.StatusCode,
        "error":        err.Error(),
        "stack_trace":  getStackTrace(),
    }).Error("External service internal error")
    
    // Отправка метрик для мониторинга
    metrics.IncErrorCounter(reqCtx.Service, reqCtx.StatusCode)
    metrics.ObserveLatency(reqCtx.Service, reqCtx.Duration)
}

4. Практические шаги по расследованию

Когда получаем internal ошибку, последовательно применяем:

  1. Проверка доступности сервиса

    • Убедиться, что ошибка не вызвана проблемами сети или инфраструктуры
    • Проверить TLS сертификаты, если используется HTTPS
    • Верифицировать DNS разрешение
  2. Анализ времени возникновения

    • Связана ли ошибка с пиками нагрузки?
    • Совпадает ли с деплоями или обновлениями?
    • Есть ли сезонность или паттерны?
  3. Исследование входных данных

    • Все ли параметры в пределах допустимых значений?
    • Не превышены ли лимиты (rate limiting, квоты)?
    • Корректны ли заголовки аутентификации?
  4. Работа с командой внешнего сервиса

    • Собрать и предоставить trace_id/request_id
    • Подготовить воспроизводимый сценарий
    • Уточнить SLA и время восстановления

5. Дизайн отказоустойчивой архитектуры

Для минимизации воздействия внутренних ошибок сторонних сервисов:

Использование кеширования:

// Многоуровневое кеширование
type CachedService struct {
    cache         *redis.Client
    localCache    *lru.Cache
    service       ExternalService
    cacheDuration time.Duration
}

func (c *CachedService) GetData(key string) (*Data, error) {
    // 1. Проверяем локальный кеш
    if data, ok := c.localCache.Get(key); ok {
        return data.(*Data), nil
    }
    
    // 2. Проверяем распределенный кеш
    if data, err := c.cache.Get(key); err == nil {
        c.localCache.Add(key, data)
        return data, nil
    }
    
    // 3. Обращаемся к сервису с защитой
    data, err := c.service.GetData(key)
    if err != nil {
        // Возвращаем устаревшие данные если есть
        return c.getStaleData(key)
    }
    
    // 4. Сохраняем в кеш
    c.cache.Set(key, data, c.cacheDuration)
    c.localCache.Add(key, data)
    
    return data, nil
}

Реализация fallback стратегий:

  • Возврат закэшированных данных
  • Использование деградированного режима работы
  • Переключение на альтернативный сервис или endpoint
  • Возврат значений по умолчанию

6. Проактивные меры

  1. Регулярное тестирование устойчивости (Chaos Engineering)
  2. Установка реалистичных таймаутов на уровне HTTP клиента
  3. Настройка алертинга на основе SLO (Service Level Objectives)
  4. Создание playbook для быстрого реагирования
  5. Периодические нагрузочные тесты зависимостей

Ключевые принципы которые я соблюдаю:

  • Никогда не игнорировать 5xx ошибки — они указывают на реальные проблемы
  • Всегда иметь план B — система должна деградировать корректно
  • Измерять всё что важно — без метрик невозможно принимать решения
  • Тестировать сценарии отказа — resilience нужно проверять до инцидента

В Go особенно важно правильно обрабатывать контексты, использовать sync.Pool для объектов запросов, и обеспечивать graceful degradation всей системы при недоступности зависимостей. Важнейший инструмент — это прометеус метрики для отслеживания количества, типа и длительности ошибок в реальном времени.

Что делать при возникновении ошибки Internal из сервиса? | PrepBro