О насБлогКонтакты
Мобильная разработка18 июля 2012 г. 4 мин 122Обновлено: 22 июня 2026 г.

GPRS-оптимизация в 2012: как мы делали мобильные API для медленного интернета

AunimedaAunimeda
📋 Содержание

Летом 2012 года мы запускали мобильное приложение для одного из бишкекских клиентов. Первые тесты на офисном Wi-Fi выглядели отлично. Первый тест с реальным телефоном в реальных условиях - GPRS на «Мегакоме» - показал загрузку главного экрана за 14 секунд.

14 секунд. На экран с 8 элементами.

Это не было проблемой сервера. Это была проблема архитектуры, которую мы спроектировали без понимания канала доставки.


Почему GPRS убивал стандартный REST

GPRS - это не просто «медленный Wi-Fi». У него другая физическая природа:

  • Пропускная способность: 40-80 кбит/с при хорошем сигнале, реально 20-40
  • Задержка (latency): 400-800 мс на один round trip
  • Потери пакетов: 2-5% в норме, 10-15% при слабом сигнале
  • Нестабильность: соединение может прерваться в середине передачи

Наш API делал 6 последовательных запросов при загрузке главного экрана. Каждый запрос - отдельный HTTP round trip. При задержке 600 мс только на установку соединений уходило 3,6 секунды ещё до передачи данных.

Запрос 1: GET /user/profile        → 600ms latency + 200ms данные
Запрос 2: GET /feed?page=1         → 600ms latency + 800ms данные  
Запрос 3: GET /notifications/count → 600ms latency + 100ms данные
...
Итого: ~8-10 секунд только на latency

Что мы изменили

1. Агрегированный endpoint для первого экрана

Вместо шести запросов - один. Сервер собирает все данные для начального рендера и возвращает их одним ответом:

// Было: клиент делает 6 запросов
// GET /user/profile, /feed, /notifications, /banners, /categories, /stats

// Стало: один endpoint с данными для старта приложения
// GET /app/bootstrap

$response = [
    'user'          => $this->getUserProfile($userId),
    'feed'          => $this->getFeedPreview($userId, 5),  // только 5 постов
    'notifications' => $this->getUnreadCount($userId),
    'categories'    => $this->getActiveCategories(),       // кэшировалось 10 минут
    'version'       => APP_VERSION,
];

6 round trips → 1. На GPRS это экономило 3-5 секунд.

2. Агрессивное сжатие

JSON без gzip на медленном канале - преступление. Мы добавили gzip с уровнем 6 (баланс скорость/степень сжатия) и убрали лишние пробелы из ответов.

// Включить gzip на сервере (nginx)
gzip on;
gzip_types application/json text/plain text/css;
gzip_comp_level 6;
gzip_min_length 256;

// В PHP: убираем лишние пробелы из JSON
echo json_encode($data, JSON_UNESCAPED_UNICODE);
// Не JSON_PRETTY_PRINT - это добавляет сотни байт

Типичный ответ с профилем пользователя: 4,2 КБ → 1,1 КБ после сжатия. На GPRS это разница в 0,8 секунды на один запрос.

3. Минимальный payload

Мы возвращали только те поля, которые клиент реально использовал. Стандартная ошибка - отдавать всю запись из БД:

// Плохо: отдаём всё
SELECT * FROM users WHERE id = ?
// → 40 полей, 2KB JSON, половина не нужна мобильному клиенту

// Хорошо: только нужное для этого экрана
SELECT id, name, avatar_url, is_verified FROM users WHERE id = ?
// → 4 поля, 180 байт JSON

Мы ввели правило: каждый endpoint имеет документированный список возвращаемых полей, и это список пересматривается с мобильной командой при каждом изменении.

4. Дельта-синхронизация вместо полной загрузки

При повторных загрузках клиент передавал timestamp последней синхронизации. Сервер отдавал только изменения:

// Клиент передаёт: ?since=1342612345
$since = (int) $_GET['since'];

if ($since > 0) {
    // Дельта: только новые/изменённые посты
    $posts = Post::where('updated_at', '>', $since)
                 ->where('user_id', $userId)
                 ->limit(20)
                 ->get();
    $mode = 'delta';
} else {
    // Первая загрузка
    $posts = Post::latest()->limit(20)->get();
    $mode = 'full';
}

echo json_encode(['mode' => $mode, 'items' => $posts, 'ts' => time()]);

После первой загрузки обновление ленты стало передавать в среднем 3-5 новых постов вместо 20. Трафик упал в 4-6 раз на повторных запросах.

5. Таймауты и fallback

GPRS-соединение может зависнуть на 30+ секунд, не вернув ни ошибки, ни данных. Клиент без таймаутов просто висит.

// Android, 2012 (Apache HttpClient, который тогда использовали все)
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 8000);  // 8 сек на коннект
HttpConnectionParams.setSoTimeout(params, 12000);          // 12 сек на чтение

// При таймауте: показываем кэшированные данные + индикатор "нет сети"
try {
    JSONObject data = apiClient.getBootstrap();
    updateUI(data);
} catch (SocketTimeoutException e) {
    JSONObject cached = localCache.getBootstrap();
    if (cached != null) {
        updateUIFromCache(cached);
        showOfflineBanner();
    } else {
        showRetryScreen();
    }
}

Результаты после оптимизации

Метрика До После
Загрузка главного экрана (GPRS) 14 сек 3,8 сек
Количество HTTP-запросов при старте 6 1
Средний размер ответа /bootstrap 18 КБ 2,2 КБ
Трафик при обновлении ленты 42 КБ 7 КБ

3,8 секунды на GPRS - всё ещё медленно по современным меркам. По меркам 2012 года в Бишкеке - достаточно быстро, чтобы пользователи не удаляли приложение.


Принцип, который остался

Тестируй на реальном устройстве в реальных условиях сети до того, как покажешь клиенту. Не на эмуляторе. Не на офисном Wi-Fi. Выйди на улицу, включи мобильный интернет, зайди в переход или подвал - и смотри как ведёт себя приложение там.

В 2012 году это было требование выживания. Сегодня, когда значительная часть пользователей Центральной Азии всё ещё работает на нестабильном 3G в районах с плохим покрытием, это требование актуально в той же мере.

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

Мобильное приложение для ресторана в Бишкеке: нужно ли и как сделатьaunimeda
Мобильная разработка

Мобильное приложение для ресторана в Бишкеке: нужно ли и как сделать

Когда ресторану в Бишкеке нужно мобильное приложение, а когда достаточно Telegram бота или виджета на сайте? Реальные сценарии, стоимость и сравнение форматов в 2026 году.

PWA или мобильное приложение: что выбрать для бизнеса в Бишкекеaunimeda
Мобильная разработка

PWA или мобильное приложение: что выбрать для бизнеса в Бишкеке

Progressive Web App или нативное мобильное приложение для бизнеса в Кыргызстане? Сравниваем возможности, стоимость и когда PWA — правильный выбор, а когда нужен Flutter.

Разработка приложения для доставки в Бишкеке: что нужно, сколько стоитaunimeda
Мобильная разработка

Разработка приложения для доставки в Бишкеке: что нужно, сколько стоит

Как разработать мобильное приложение для доставки еды или товаров в Бишкеке: функционал, три мобильных приложения, технологии, стоимость и реальные сроки в 2026 году.

Нужна IT-разработка для вашего бизнеса?

Разрабатываем сайты, мобильные приложения и AI-решения для бизнеса в Кыргызстане. Бесплатная консультация.

Мобильные приложения

Получить консультацию Все статьи