Лекция 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, общие заголовки, таймауты и базовую обработку ошибок.
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 вручную.
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.1 | HTTP/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
| Критерий | JSON | Protocol 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. Сравнение механизмов аутентификации
| Критерий | JWT | OAuth 2.0 | API 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.
Вопросы для самопроверки
-
Какие особенности мобильных сетей (нестабильность, переключение сетей, ограничения батареи и трафика) влияют на архитектуру сетевого слоя и как именно?
-
Для чего нужны клиент API, слой сервисов и интерсепторы? Какую задачу решает каждый из этих компонентов?
-
В чём преимущества HTTP/2 над HTTP/1.1 для мобильных приложений? Какую дополнительную проблему решает HTTP/3 (QUIC)?
-
Сравните JWT, OAuth 2.0 и API Keys: что это по своей сути, какие плюсы и минусы у каждого и когда их применять?