Retry Policy в мобильной разработке — суть, стратегии и принцип

Автор: IT Sectr Опубликовано: 2026-03-11 Время чтения: 10 мин

Retry Policy — политика повторных запросов — набор правил, определяющих, когда и как мобильное приложение автоматически повторяет неудачные сетевые вызовы. При нестабильном соединении или временных ошибках сервера грамотная политика повторов повышает надёжность приложения без участия пользователя. По данным исследования Google Developer Relations (2025), правильная реализация Retry Policy снижает процент потерянных запросов на 40-60% в мобильных приложениях с частыми сетевыми операциями.

Главное

  • Retry Policy — стратегия автоматических повторов запросов при сбоях сети или временных ошибках сервера.
  • Exponential backoff — метод увеличения задержки между повторами для снижения нагрузки на сервер.
  • Jitter — случайное отклонение задержки, предотвращающее эффект "гром среди ясного неба" (thundering herd).
  • Идемпотентность — ключевое требование для безопасных повторов: повторный запрос не должен вызывать побочных эффектов.
  • Circuit Breaker — механизм остановки повторов при длительной недоступности сервиса для сохранения ресурсов.

Что такое Retry Policy?

Retry Policy — это программная стратегия, определяющая поведение клиента при сбое сетевого запроса: какие ошибки стоит повторять, сколько раз, с какой задержкой и когда прекратить попытки. В мобильных приложениях политика повторов критически важна из-за нестабильности мобильных сетей и возможных временных сбоев на стороне сервера.

Базовая Retry Policy включает три параметра: максимальное количество повторов (maxRetries), начальная задержка (baseDelay) и стратегия увеличения задержки (backoff strategy). Дополнительно может быть задан список HTTP-статусов, на которые следует реагировать повтором, и таймаут для прерывания всех попыток.

По данным книги "Designing Data-Intensive Applications" Мартина Клеппмана, 50% сбоев в распределённых системах являются временными и устраняются при повторной попытке. Это делает Retry Policy одним из самых эффективных и дешёвых способов повышения отказоустойчивости мобильного приложения без изменений серверной архитектуры.

Какие ошибки стоит повторять

Временные ошибки (retriable) — единственный тип сбоев, на которые Retry Policy должна реагировать. К ним относятся таймауты соединения (SocketTimeoutException), временная недоступность сервера (HTTP 503, 502) и ошибки DNS. Постоянные ошибки — HTTP 400, 401, 403, 404 — повторять бессмысленно, так как они указывают на проблему в запросе, а не в сети или сервере.

По данным исследования AWS Architecture Blog, правильная классификация ошибок на retriable и non-retriable — самое важное решение при проектировании Retry Policy. Повтор неидемпотентного запроса на HTTP 401 может привести к блокировке аккаунта, а повтор HTTP 400 — к созданию дубликатов данных. Всегда конфигурируйте список кодов для повтора явно.

Основные стратегии повторов

Fixed interval — самая простая стратегия: каждый повтор выполняется через одинаковый промежуток времени. Например, при задержке 2 секунды приложение повторяет запрос через 2, 2, 2 секунды. Fixed interval прост в реализации и предсказуем, но создаёт равномерную нагрузку на сервер при массовых сбоях.

Incremental interval — задержка увеличивается линейно с каждым повтором: первый повтор через 1 секунду, второй — через 2, третий — через 3 и так далее. Эта стратегия даёт серверу больше времени на восстановление при повторных сбоях, но всё ещё предсказуема для множества одновременно падающих клиентов.

СтратегияФормула задержкиКумулятивное время (3 попытки)Применение
Fixeddelay = D3 × DПростые сценарии, локальные таймауты
Incrementaldelay = N × D6 × DПостепенное снижение нагрузки
Exponentialdelay = D × 2^N7 × DМассовые сбои, облачные сервисы
Exponential + Jitterdelay = random(0, D × 2^N)variesВысокая нагрузка, микросервисы

Выбор стратегии зависит от характера приложения. Для фоновых задач синхронизации данных на мобильных устройствах оптимальна exponential стратегия с jitter — она даёт наибольшую вероятность успеха при минимальной нагрузке на сервер и устройство пользователя.

Exponential Backoff и Jitter

Exponential backoff — стратегия, при которой задержка между повторами удваивается с каждой попыткой. Если начальная задержка составляет 1 секунду, то последовательность задержек будет 1, 2, 4, 8, 16 секунд. Это даёт серверу экспоненциально растущее время на восстановление.

Jitter — случайное отклонение задержки, которое предотвращает синхронные повторные запросы от множества клиентов (thundering herd problem). Без jitter тысяча клиентов с одинаковой Retry Policy будет повторять запросы одновременно, создавая пиковую нагрузку на сервер. Jitter разносит повторы во времени.

Реализация на Kotlin с корутинами

Корутины Kotlin позволяют реализовать exponential backoff с jitter без блокировки основного потока. Функция retry из kotlinx-coroutines принимает условие повтора и блок с телом запроса, автоматически управляя задержками и количеством попыток.

kotlin
suspend fun RetryPolicy.executeWithRetry(
    block: suspend () -> Result<T>
): Result<T> {
    var lastError: Throwable? = null
    repeat(maxRetries + 1) { attempt ->
        try {
            return block()
        } catch (e: Exception) {
            if (!isRetriable(e) || attempt == maxRetries) {
                return Result.failure(e)
            }
            val delay = (baseDelayMs * (1 shl attempt))
                .toLong()
            val jitteredDelay = (delay * (0.5 + Random.nextDouble())).toLong()
            delay(jitteredDelay)
            lastError = e
        }
    }
    return Result.failure(lastError!!)
}

Функция executeWithRetry принимает лямбду с сетевым вызовом и выполняет её с exponential backoff и jitter. Если ошибка не подлежит повтору (не retriable) или превышено максимальное число попыток, функция возвращает ошибку. Задержка домножается на случайный коэффициент от 0.5 до 1.5 для равномерного распределения повторов.

Circuit Breaker и отказ от повторов

Circuit Breaker — паттерн проектирования, который предотвращает бесконечные повторные запросы при длительной недоступности сервиса. Когда количество ошибок превышает порог, Circuit Breaker переходит в состояние OPEN и немедленно возвращает ошибку без выполнения запроса, давая серверу время на восстановление.

В мобильных приложениях Circuit Breaker особенно полезен при недоступности API из-за планового обслуживания или сетевых сбоев оператора. Без него приложение будет потреблять батарею и трафик на бесконечные повторные попытки, ухудшая пользовательский опыт и сокращая время автономной работы устройства.

Схема состояний Circuit Breaker

Три состояния Circuit Breaker: CLOSED (нормальная работа, запросы выполняются), OPEN (отказ, запросы блокируются) и HALF_OPEN (пробный запрос для проверки восстановления). После заданного таймаута в состоянии OPEN переключатель переходит в HALF_OPEN и выполняет один запрос — при успехе возвращается в CLOSED, при неудаче — в OPEN.

kotlin
class CircuitBreaker(
    private val failureThreshold: Int = 3,
    private val timeoutMs: Long = 30000
) {
    private var state = State.CLOSED
    private var failureCount = 0
    private var lastFailureTime: Long = 0

    suspend fun T.protect(block: suspend () -> T): T {
        checkState()
        return try {
            val result = block()
            onSuccess()
            result
        } catch (e: Exception) {
            onFailure()
            throw e
        }
    }
}

Реализация Circuit Breaker на Kotlin содержит счётчик ошибок и таймер восстановления. Метод protect проверяет текущее состояние перед выполнением блокируемого запроса и обновляет счётчик ошибок при сбоях. После достижения порога failureThreshold все запросы немедленно отклоняются до истечения timeoutMs.

Retry Policy в мобильных приложениях

Мобильные сети имеют особенности, которые делают Retry Policy особенно важной. Переключение между Wi-Fi и мобильными данными, потеря сигнала в метро и тоннелях, временные блокировки на уровне оператора — все эти сценарии приводят к сбоям запросов, которые успешно обрабатываются повторными попытками.

На Android библиотека Retrofit и OkHttp предоставляют встроенный механизм RetryPolicy через Interceptor. На iOS задача решается через URLSessionConfiguration и кастомное делегирование. Для кросс-платформенной разработки Ktor (KMP) содержит встроенную поддержку retry с настраиваемыми стратегиями.

Реализация на iOS с Combine

Combine — фреймворк Apple для реактивного программирования. Оператор retry в Combine повторяет паблишер указанное количество раз при ошибке, но не позволяет настраивать задержку между повторами. Для полноценной Retry Policy используется кастомная комбинация catch и flatMap с задержкой.

swift
extension Publisher {
    func retryWithBackoff(
        retries: Int = 3,
        baseDelay: TimeInterval = 1.0
    ) -> AnyPublisher<Output, Failure> {
        return self.catch { error -> AnyPublisher in
            guard retries > 0 else {
                return Fail(error).eraseToAnyPublisher()
            }
            return Just(())
                .delay(for: .seconds(baseDelay), scheduler: DispatchQueue.main)
                .flatMap { self.retryWithBackoff(
                    retries: retries - 1,
                    baseDelay: baseDelay * 2
                ) }
                .eraseToAnyPublisher()
        }
        .eraseToAnyPublisher()
    }
}

Расширение retryWithBackoff для Publisher в Combine реализует exponential backoff через рекурсивный вызов с уменьшением счётчика и удвоением задержки. Оператор delay создаёт паузу между повторами, а catch перехватывает ошибку и решает, пытаться ли снова или вернуть failure.

Типовые ошибки при Retry Policy

Первая ошибка — повторять запросы без проверки идемпотентности. Если сервер создал ресурс, но не вернул подтверждение из-за сбоя сети, повторный запрос создаст дубликат. Для POST-запросов всегда используйте идемпотентный ключ (Idempotency-Key) в заголовке или переходите на retry только для GET, PUT и DELETE.

Вторая ошибка — бесконечные повторы (retry forever). Всегда устанавливайте максимальное количество попыток (3-5 для мобильных приложений) и общий таймаут на все попытки. Бесконечные повторы разряжают батарею и создают паразитную нагрузку на сервер, особенно при миграции баз данных или изменении API.

Третья ошибка — игнорировать контекст приложения. Если пользователь закрыл приложение или перешёл в фоновый режим, активные Retry Policy должны корректно отменяться. Используйте корутины с SupervisorScope или Combine с жизненным циклом UI для автоматической отмены повторов при закрытии экрана.

Четвёртая ошибка — не логировать повторные попытки. Без логирования вы не узнаете, сколько запросов было повторено, какие ошибки возникали и насколько эффективна ваша Retry Policy. Добавьте метрики: количество повторов, успешность после повтора, распределение задержек. Эти данные помогут настроить оптимальные параметры стратегии под конкретное приложение.

Часто задаваемые вопросы

Сколько раз повторять запрос в мобильном приложении?

Оптимальное количество повторов — 3-5 попыток для большинства сценариев. Для фоновой синхронизации допустимо 5-7 попыток, для интерактивных запросов (например, отправка формы) — не более 3. Большее количество повторов не увеличивает вероятность успеха, но расходует батарею и трафик пользователя.

Что такое exponential backoff простыми словами?

Exponential backoff — это удвоение задержки между повторными попытками: 1 секунда, 2, 4, 8, 16 и так далее. Если сервер перегружен, короткая пауза между первыми повторами позволяет ему быстро ответить, а растущая пауза с каждым следующим повтором даёт серверу всё больше времени на восстановление.

Какие HTTP-статусы стоит повторять?

Повторяйте только временные ошибки: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Ошибки 4xx (кроме 408 и 429) указывают на проблемы клиента — повторять их бессмысленно и может быть опасно для данных пользователя.

Чем отличается Retry Policy от Circuit Breaker?

Retry Policy управляет повторением одного запроса при сбое. Circuit Breaker управляет состоянием соединения с сервисом: при накоплении ошибок он размыкает цепь (OPEN) и не допускает новые запросы. Retry работает на уровне отдельного вызова, Circuit Breaker — на уровне интеграции с сервисом.

Как тестировать Retry Policy на мобильных устройствах?

Для тестирования Retry Policy используйте NetworkInterceptor (OkHttp) на Android и URLProtocol (URLSession) на iOS для симуляции сбоев сети. Задайте параметры: частота ошибок, длительность недоступности и коды ответов. Юнит-тесты с MockWebServer (OkHttp) или OHHTTPStubs (iOS) проверяют логику повторов без реальной сети.

Итоги

  • Retry Policy — стратегия автоматических повторов сетевых запросов при временных сбоях с настраиваемыми параметрами задержки и количества попыток.
  • Exponential backoff с jitter — базовая стратегия для мобильных приложений, снижающая нагрузку на сервер при массовых сбоях и предотвращающая эффект thundering herd.
  • Идемпотентность — обязательное условие для безопасного повторения не-GET запросов: без неё повтор создаёт дубликаты данных или нежелательные побочные эффекты.
  • Circuit Breaker дополняет Retry Policy, предотвращая бесконечные повторы при длительной недоступности сервиса и экономя ресурсы устройства.
  • Классификация ошибок — разделение на retriable (503, 502, timeout) и non-retriable (400, 401, 403) критически важно для корректной работы политики повторов.
  • Максимум 3-5 повторов в интерактивных сценариях и до 7 для фоновой синхронизации — оптимальное значение для мобильных приложений по данным Google Developer Relations.
  • Рекомендация — реализуйте Retry Policy с exponential backoff, Circuit Breaker и логированием для всех сетевых запросов в мобильном приложении.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также