Перейти к содержимому

Лекция 12. Сетевое взаимодействие. Часть 1: основы, протоколы, аутентификация

Введение

Практически любое современное мобильное приложение бесполезно без сети: лента новостей, мессенджер, карта, банковское приложение, интернет-магазин — все они получают и отправляют данные на удалённые серверы. Поэтому сетевой слой — одна из самых важных частей архитектуры приложения.

Но мобильная сеть устроена принципиально иначе, чем сеть стационарного компьютера. Соединение нестабильно, пользователь постоянно перемещается, батарея и мобильный трафик ограничены. Эти ограничения нельзя игнорировать — их нужно закладывать в архитектуру с самого начала.

В этой лекции мы разберём:

  • особенности мобильных сетей и их влияние на проектирование;
  • архитектурные паттерны сетевого слоя (клиент API, слой сервисов, интерсепторы);
  • транспортные протоколы: HTTP/1.1, HTTP/2, HTTP/3 (QUIC);
  • форматы сериализации: JSON и Protocol Buffers;
  • механизмы аутентификации: JWT, OAuth 2.0, API Keys.

Часть 2 (следующая лекция) будет посвящена практике: клиент fetch/axios, обработка ошибок, повторные попытки, кэширование и offline-first.


1. Особенности мобильных сетей

Напомним стек: сетевое взаимодействие строится по модели TCP/IP. Для нас важны верхние уровни — транспортный (L4): TCP (надёжная доставка с гарантией порядка) и UDP (быстрая доставка без гарантий, основа QUIC); прикладной (L7): HTTP/HTTPS для REST API, WebSocket для real-time, gRPC поверх HTTP/2. В работе мы обращаемся к прикладному уровню.

1.1. Чем мобильная сеть отличается от стационарной

Нестабильность соединения. В метро, лифте, за городом сигнал то появляется, то пропадает. Текущий запрос может прерваться на середине. Приложение должно корректно переживать обрывы, а не «зависать» навсегда.

Переключение между сетями. Выходя из дома, телефон переключается с Wi-Fi на сотовую сеть (4G/5G). Меняется IP-адрес, рвутся открытые TCP-соединения — долгие загрузки приходится начинать заново.

Высокая и переменная задержка (latency, jitter). В сотовых сетях задержка до сервера достигает сотен миллисекунд и постоянно меняется. Множество мелких последовательных запросов сильно замедляют приложение.

Ограничения батареи. Радиомодуль — один из главных потребителей энергии. Каждое «пробуждение» радио расходует заряд, поэтому частый поллинг сервера незаметно сажает батарею.

Ограничения трафика. У многих пользователей лимитированный тариф — передача лишних данных стоит им денег.

1.2. Как это влияет на архитектуру

Особенность сетиСледствие для архитектуры
Обрывы соединенияТаймауты, повторные попытки, идемпотентность операций
Переключение сетейОтслеживание состояния сети, восстановление соединений
Высокая задержкаОбъединение запросов (батчинг), HTTP/2, кэширование
Ограничения батареиМинимум запросов, избегать поллинга, push-уведомления
Ограничения трафикаСжатие, компактные форматы, запрос только нужных данных

Главный вывод: сеть — ненадёжный ресурс, и приложение проектируется в расчёте на то, что она может отказать в любой момент.


2. Архитектурные паттерны сетевого слоя

Плохая практика — раскидывать вызовы fetch прямо по компонентам экранов: URL, заголовки и обработка ошибок дублируются повсюду, а при смене API приходится править десятки файлов. Сетевой код выделяют в отдельный слой.

2.1. Клиент API (API client)

Клиент API — единая точка, через которую приложение общается с сервером. Он инкапсулирует базовый URL, общие заголовки, таймауты и базовую обработку ошибок.

api/client.js
import axios from 'axios';
export const apiClient = axios.create({
baseURL: 'https://api.example.com/v1',
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
});

Теперь весь остальной код использует apiClient и не знает деталей о базовом адресе или таймаутах. Сменился сервер — поправили одну строку.

2.2. Слой сервисов (service layer)

Сервис — модуль, который группирует запросы по предметной области и скрывает структуру эндпоинтов за понятными функциями. Компонент вызывает userService.getProfile(id), а не строит URL вручную.

api/userService.js
import { apiClient } from './client';
export const userService = {
getProfile: (id) => apiClient.get(`/users/${id}`),
updateProfile: (id, data) => apiClient.put(`/users/${id}`, data),
getPosts: (id) => apiClient.get(`/users/${id}/posts`),
};

Преимущества: компоненты не зависят от формата URL; логику легко переиспользовать и тестировать (сервис подменяется заглушкой); при изменении API правки локализованы в одном модуле.

2.3. Интерсепторы (interceptors)

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

// Добавляем токен авторизации в каждый запрос
apiClient.interceptors.request.use((config) => {
const token = getAuthToken();
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// Единая обработка ошибки 401 (истёк токен)
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) redirectToLogin();
return Promise.reject(error);
},
);

Типичные задачи интерсепторов: подстановка токена, логирование, обновление истёкшего токена, единообразная обработка ошибок. Без них этот код пришлось бы повторять в каждом запросе. Итоговая структура слоя: компонент → сервис → клиент API (+ интерсепторы) → сеть — каждый уровень отвечает за своё.


3. Протоколы передачи данных

3.1. HTTP/1.1

HTTP/1.1 (1997) до сих пор широко используется. Ключевые механизмы: Keep-Alive (переиспользование TCP-соединения для нескольких запросов), сжатие тела (gzip, brotli), кэширование (Cache-Control, ETag, Last-Modified).

Главная проблема — head-of-line blocking: по одному соединению запросы идут по очереди, и медленный запрос задерживает следующие. Браузеры обходят это, открывая несколько параллельных соединений, но каждое — лишний расход ресурсов и батареи на мобильном устройстве.

3.2. HTTP/2

HTTP/2 (2015) сохраняет ту же семантику (методы, статусы, заголовки), но меняет способ передачи данных «по проводу»:

  • Мультиплексирование — множество запросов и ответов передаются параллельно по одному TCP-соединению, не блокируя друг друга. Это снимает проблему head-of-line blocking на уровне HTTP.
  • Сжатие заголовков (HPACK) — заголовки (которые в мобильных API часто повторяются: токены, user-agent) сжимаются, экономя трафик.
  • Бинарный формат вместо текстового — быстрее и надёжнее парсится.
  • Server Push — сервер может заранее отправить ресурсы (на практике редко).

Выгода для мобильных: одно соединение вместо нескольких — меньше нагрузки на радиомодуль и батарею; параллельные запросы по медленной сети идут быстрее; сжатие заголовков экономит трафик.

3.3. Сравнение HTTP/1.1 и HTTP/2

ХарактеристикаHTTP/1.1HTTP/2
ФорматТекстовыйБинарный
Запросы в соединенииПо очереди (1 за раз)Параллельно (мультиплексирование)
Head-of-line blockingЕсть (на уровне HTTP)Нет (на уровне HTTP)
Сжатие заголовковНетЕсть (HPACK)
Число соединенийНесколько параллельныхОбычно одно
Server PushНетЕсть

3.4. HTTP/3 и QUIC

У HTTP/2 остаётся слабость: он работает поверх TCP, и при потере пакета TCP останавливает все потоки до его повторной передачи — head-of-line blocking возвращается на транспортном уровне, что особенно заметно в мобильных сетях.

HTTP/3 решает проблему, работая поверх протокола QUIC (на базе UDP):

  • потеря пакета влияет только на свой поток, остальные продолжают передаваться;
  • встроенное шифрование (TLS 1.3 «вшит» в протокол) — соединение устанавливается за меньшее число round-trip;
  • миграция соединения — при смене сети (Wi-Fi ↔ сотовая) соединение сохраняется по идентификатору, а не по паре IP-адресов, и не рвётся.

Именно миграция соединения делает HTTP/3 особенно ценным для мобильных. На стороне клиента это работает прозрачно — поддержку обеспечивают ОС и библиотеки.


4. Форматы сериализации данных

Перед отправкой по сети данные сериализуют — превращают объект в последовательность байтов. Выбор формата влияет на размер трафика, скорость и удобство разработки.

4.1. JSON

JSON (JavaScript Object Notation) — текстовый формат, де-факто стандарт для REST API:

{ "id": 123, "name": "Анна", "isActive": true }

Достоинства: человекочитаемость (легко отлаживать), нативная поддержка в JavaScript (JSON.parse / JSON.stringify), универсальная поддержка во всех языках.

Недостатки: избыточный размер (имена полей повторяются, всё хранится как текст), относительно медленный парсинг больших объёмов, нет встроенной схемы — структуру проверяют вручную.

4.2. Protocol Buffers

Protocol Buffers (protobuf) — бинарный формат от Google со строгой схемой. Структура данных описывается заранее в .proto-файле:

message User {
int32 id = 1;
string name = 2;
bool is_active = 3;
}

По схеме генерируется код для сериализации. В бинарном виде поля кодируются номерами (1, 2, 3), а не текстовыми именами, поэтому данные занимают значительно меньше места.

Достоинства: компактность (в 3–10 раз меньше JSON), быстрая сериализация и парсинг, строгая типизированная схема с контролем обратной совместимости версий.

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

4.3. Сравнение JSON и Protocol Buffers

КритерийJSONProtocol Buffers
ПредставлениеТекстБинарное
РазмерБольшеМеньше (в 3–10 раз)
Скорость парсингаНижеВыше
Читаемость человекомДаНет
СхемаНет (опционально)Обязательна (.proto)
Поддержка в браузереНативнаяЧерез библиотеку
ОтладкаПростаяСложнее

Когда что выбирать. JSON — выбор по умолчанию: прост, универсален, подходит для большинства REST API. Protocol Buffers оправдан, когда критичны размер и скорость: высоконагруженные сервисы, gRPC, передача больших объёмов по медленной сети. Промежуточный вариант — MessagePack (компактный бинарный формат, но без обязательной схемы).


5. Механизмы аутентификации

Аутентификация — подтверждение того, кто делает запрос. Рассмотрим три распространённых подхода.

5.1. JWT (JSON Web Token)

JWT — самодостаточный токен, внутри которого закодирована информация о пользователе. Состоит из трёх частей, разделённых точками: header.payload.signature.

  • Header — алгоритм подписи и тип токена.
  • Payload — данные (claims): идентификатор пользователя, роли, время истечения exp.
  • Signature — подпись, которой сервер проверяет, что токен не подделан.

Важно: payload не зашифрован, а лишь закодирован (Base64URL) — секретные данные в него класть нельзя, их может прочитать кто угодно. Подпись гарантирует только целостность. Сервер работает stateless: не хранит сессии, а лишь проверяет подпись и срок действия — это упрощает масштабирование.

  • Плюсы: не требует хранения сессий, легко масштабируется, токен переносит данные о пользователе.
  • Минусы: токен нельзя мгновенно отозвать (действителен до exp); скомпрометированный токен опасен до истечения. Частичное решение — короткоживущий access-токен + долгоживущий refresh-токен.

5.2. OAuth 2.0

OAuth 2.0 — это не способ хранения токена, а протокол делегированной авторизации. Он позволяет приложению получить доступ к ресурсам пользователя на другом сервисе без передачи пароля. Именно OAuth 2.0 стоит за кнопками «Войти через Google / Apple / VK».

Для мобильных приложений используется Authorization Code Flow с PKCE (Proof Key for Code Exchange): приложение открывает страницу входа провайдера, пользователь подтверждает доступ, приложение получает временный код авторизации и обменивает его на access-токен (часто это тот же JWT). PKCE защищает обмен — по сгенерированному секрету провайдер убеждается, что код обменивает именно то приложение, которое его запросило (мобильный клиент не может надёжно хранить общий client secret).

  • Плюсы: пользователь не передаёт пароль приложению; гибкие права доступа (scopes); стандарт индустрии.
  • Минусы: сложнее в реализации; зависимость от внешнего провайдера.

5.3. API Keys

API-ключ — простая строка-идентификатор, которую приложение или сервис передаёт в заголовке запроса. Чаще применяется для доступа к публичным или сервисным API (карты, погода, аналитика), а не для аутентификации конкретного пользователя.

  • Плюсы: предельно прост в использовании.
  • Минусы: не идентифицирует пользователя; при утечке даёт полный доступ; ключ нельзя безопасно хранить в коде приложения (его извлекают из бандла) — нужна ротация, ограничения по доменам/IP и проксирование через свой бэкенд.

5.4. Сравнение механизмов аутентификации

КритерийJWTOAuth 2.0API Keys
Что этоФормат токенаПротокол авторизацииПростой ключ
ИдентифицируетПользователяПользователя (делегировано)Приложение/сервис
Состояние на сервереНет (stateless)ЗависитНет
СложностьСредняяВысокаяНизкая
Отзыв доступаСложно (до exp)ПоддерживаетсяСмена ключа
Типичное применениеСессии пользователяВход через соцсетиДоступ к публичным API

На практике подходы часто комбинируются: вход через OAuth 2.0, а далее сессия поддерживается через JWT (access + refresh токены).


Краткие итоги

  • Мобильная сеть ненадёжна: обрывы, переключение сетей, высокая задержка, ограничения батареи и трафика. Это закладывается в архитектуру с самого начала.
  • Сетевой код выносится в отдельный слой: клиент API (общие настройки), сервисы (запросы по доменам) и интерсепторы (сквозная логика — токены, ошибки, логирование).
  • HTTP/2 даёт мультиплексирование и сжатие заголовков — меньше соединений, меньше расход батареи, выше скорость. HTTP/3 (QUIC) добавляет устойчивость к потерям пакетов и миграцию соединения при смене сети.
  • JSON — универсальный выбор по умолчанию; Protocol Buffers — когда критичны размер и скорость (бинарный формат со схемой).
  • Аутентификация: JWT — stateless-токены для сессий; OAuth 2.0 — делегированная авторизация (вход через соцсети) с PKCE; API Keys — простой доступ к сервисным API.

Вопросы для самопроверки

  1. Какие особенности мобильных сетей (нестабильность, переключение сетей, ограничения батареи и трафика) влияют на архитектуру сетевого слоя и как именно?

  2. Для чего нужны клиент API, слой сервисов и интерсепторы? Какую задачу решает каждый из этих компонентов?

  3. В чём преимущества HTTP/2 над HTTP/1.1 для мобильных приложений? Какую дополнительную проблему решает HTTP/3 (QUIC)?

  4. Сравните JWT, OAuth 2.0 и API Keys: что это по своей сути, какие плюсы и минусы у каждого и когда их применять?