Итератор

Бэкенд

Девять модулей о том, что происходит между HTTP-запросом и ответом. Направление закончено: все разборы написаны, вычитаны и больше не меняются, кроме исправления ошибок.

9 модулей52 разбора112 задач~60 часовготово
Для кого

Если вы пишете серверный код и местами угадываете

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

Типичный читатель: разработчик с одним-двумя годами опыта, который устал от ответов вида «так принято» и хочет понимать, откуда берутся таймауты, почему ретрай иногда опаснее отказа и что на самом деле делает connection pool.

Что нужно знать заранее

Любой язык на уровне «могу написать и запустить программу». Базовое представление о клиент-серверной модели. Полезно, но не обязательно — первые четыре модуля направления «Сети».

Чего здесь нет

Конкретных фреймворков, деплоя, Docker и Kubernetes, фронтенда. Всё это меняется каждые три года, а механика под ними — нет.

Программа

Девять модулей

Порядок рекомендуемый. У каждого модуля указано, что нужно знать до него.

01

HTTP как протокол, а не как библиотека

Структура запроса и ответа побайтово. Методы и их семантика: безопасность, идемпотентность, кэшируемость. Коды состояния, которые все путают: 401 против 403, 400 против 422, 301 против 308. Заголовки, которые действительно важны. Персистентные соединения, pipelining и почему он не взлетел. HTTP/2: мультиплексирование и сжатие заголовков. HTTP/3 и зачем ему QUIC.

7 разборов · 14 задач · без предварительных знаний

02

Жизненный цикл запроса на сервере

От accept() до отправки последнего байта. Модели обработки: процесс на соединение, поток на соединение, событийный цикл, пул потоков. Чем реально отличается блокирующий ввод-вывод от неблокирующего. Backlog, EADDRINUSE и прочие ошибки, которые пугают в первый раз. Как сервер отдаёт большие файлы и что такое sendfile.

6 разборов · 11 задач · нужен модуль 01

03

Конкурентность без мистики

Параллелизм против конкурентности. Потоки ОС, зелёные потоки, корутины, async/await — одна и та же идея в разных упаковках. Гонки данных на конкретных примерах. Мьютексы, семафоры, каналы. Дедлок, лайвлок и голодание. Почему «просто добавить мьютекс» обычно делает хуже, и как замерить, стало ли лучше.

8 разборов · 19 задач · нужен модуль 02

04

Состояние: где его держать

Stateless не значит «без состояния» — значит «состояние не в процессе». Сессии: в памяти, в хранилище, в токене. JWT: когда уместен и почему его так часто применяют не по делу. Sticky sessions и их цена. Миграции состояния при выкладке новой версии.

5 разборов · 9 задач · нужен модуль 02

05

Кэширование

Четыре уровня кэша на пути запроса и кто за каждый отвечает. HTTP-кэширование: Cache-Control, ETag, условные запросы. Кэш приложения: стратегии чтения и записи, инвалидация. Проблема холодного старта, cache stampede и три способа его избежать. Почему инвалидация кэша — действительно трудная задача, с конкретными примерами, а не как шутка.

6 разборов · 12 задач · нужны модули 01 и 04

06

Очереди и фоновая работа

Зачем выносить работу из запроса. Гарантии доставки: at-most-once, at-least-once, exactly-once и почему последнее существует только с оговорками. Очереди задач и брокеры сообщений — разница. Порядок сообщений, партиционирование, dead letter queue. Отложенные задачи и расписания.

6 разборов · 13 задач · нужен модуль 03

07

Идемпотентность и повторы

Главный модуль направления, если судить по письмам читателей. Почему ретрай без идемпотентности превращает один платёж в три. Ключи идемпотентности: как их выдавать, где хранить, когда удалять. Экспоненциальная задержка и джиттер. Таймауты: клиентские, серверные и бюджет времени на весь запрос. Circuit breaker.

5 разборов · 14 задач · нужны модули 03 и 06

08

Наблюдаемость

Логи, метрики и трассировки — что из них для чего. Структурированное логирование и что нельзя писать в лог. Типы метрик и почему среднее время ответа вам врёт: перцентили, гистограммы, хвосты распределения. Распределённая трассировка и контекст запроса. Что такое SLI и как из него получается полезный алерт.

5 разборов · 10 задач · нужны модули 02 и 06

09

Безопасность прикладного уровня

Аутентификация против авторизации. Хранение паролей: почему именно медленные хеши. OAuth 2.1 и OpenID Connect — схемы потоков без маркетинга. Инъекции всех видов и единственный надёжный способ их избежать. CORS, CSRF и SameSite. Ограничение частоты запросов. Что логировать при подозрительной активности, а что — никогда.

4 разбора · 10 задач · нужны модули 01 и 04

Пример

Как выглядит материал внутри

Фрагмент из модуля 07 — ключ идемпотентности и его обработка на сервере.

// Клиент генерирует ключ один раз на операцию
// и повторяет его при всех ретраях.
POST /payments HTTP/1.1
Idempotency-Key: 9f1c4b2e-...-a7
Content-Type: application/json

{ "amount": 4200, "currency": "RUB" }
func handle(req Request) Response {
  key := req.Header("Idempotency-Key")
  if key == "" {
    return BadRequest("key required")
  }
  // Атомарная вставка: выигрывает первый
  ok, prev := store.Claim(key, req.Hash())
  if !ok {
    return prev.Response  // тот же ответ
  }
  resp := charge(req)
  store.Commit(key, resp)
  return resp
}
На что обратить внимание

Ключ захватывается до выполнения операции, а не после. Если списание упадёт после Claim, но до Commit, повтор увидит незавершённую запись — и именно для этого случая в разборе разбираются три стратегии: вернуть 409, дождаться завершения или выполнить заново, пометив предыдущую попытку как потерянную.