Сети и протоколы
Критический путь рендеринга и Core Web Vitals
Сетевая часть загрузки страницы занимает 150 мс. LCP срабатывает через 3,2 секунды. Разрыв — это конвейер рендеринга браузера, и большая его часть вызвана вашими собственными решениями: какой CSS загружать, где разместить скрипты, есть ли у изображений размеры, какой JavaScript выполняется в главном потоке до отрисовки. Понимание этого конвейера — вторая половина оптимизации загрузки страницы.
Критический путь рендеринга
После получения HTML-байтов рендеринг происходит в несколько перекрывающихся этапов:
- Разбор HTML → DOM. HTML-парсер строит Document Object Model по мере поступления байтов. Разбор начинается до получения полного тела — потоковый разбор.
- Разбор CSS → CSSOM. Когда браузер встречает тег
<link rel="stylesheet">, он загружает CSS-файл и строит CSS Object Model. Ничего не отрисовывается до готовности CSSOM — CSS блокирует рендеринг. Почему? Показать страницу без стилей (FOUC — вспышка нестилизованного контента) и затем перерисовать её вызывает нестабильность макета, которая снижает воспринимаемую производительность. Это намеренно, а не ошибка. - DOM + CSSOM → дерево рендеринга. Браузер объединяет DOM и CSSOM в дерево рендеринга, содержащее только видимые элементы с вычисленными стилями.
- Компоновка (Layout). Браузер вычисляет точное положение и размер каждого элемента.
- Отрисовка (Paint). Браузер растеризует дерево рендеринга в пиксели.
- Композитинг (Compositing). GPU-слои объединяются в финальный кадр.
JavaScript усложняет процесс: тег <script> без async или defer блокирует разбор HTML. Парсер останавливается, скрипт загружается и выполняется, затем разбор продолжается. Именно поэтому классические скрипты в <head> резко замедляют первую отрисовку.
| Тип ресурса | Блокирует разбор HTML? | Блокирует рендеринг? | Решение |
|---|---|---|---|
| CSS (stylesheet) | Нет | Да | встроить критический CSS; preload |
| JS (classic, в <head>) | Да | Да | добавить defer или async |
| JS (async) | Частично | Частично | выполняется по готовности, не по порядку |
| JS (defer) | Нет | Нет | выполняется после разбора HTML, по порядку |
| Изображения (без размеров) | Нет | Вызывает CLS | добавить атрибуты width/height |
| Шрифты (WOFF2) | Нет | Вызывает FOIT/FOUT | preload + font-display:swap |
Preload scanner
Браузер запускает лёгкий HTML-токенизатор, называемый preload scanner, опережая основной парсер. Он ищет URL в <link>, <script> и <img> и отправляет запросы до того, как основной парсер обработает эти теги. Без него обнаружение подресурсов было бы последовательным — каждый ресурс загружается только после того, как основной парсер достигает соответствующего тега.
Preload scanner выводится из строя:
- JavaScript, записывающим
<script src="...">черезdocument.write. - Сложной клиентской маршрутизацией, которая формирует URL после инициализации фреймворка.
- Схемами ленивой загрузки, скрывающими URL за атрибутами
data-src.
Современные фреймворки (Next.js, Astro, SvelteKit) автоматически генерируют <link rel="preload"> для компенсации.
Resource hints
| Подсказка | Эффект | Стоимость |
|---|---|---|
<link rel="dns-prefetch" href="//cdn.example.com"> | Разрешает DNS в фоне | Минимальная |
<link rel="preconnect" href="//cdn.example.com" crossorigin> | DNS + TCP + TLS в фоне | Один простаивающий сокет на источник |
<link rel="preload" as="font" crossorigin> | Загружает шрифт до достижения тега парсером | Пропускная способность |
<link rel="modulepreload"> | Загружает ES-модуль + его импорты | Пропускная способность |
<link rel="prefetch"> | Загружает ресурс следующей навигации (низкий приоритет) | Пропускная способность в простое |
Атрибут fetchpriority. Можно переопределить приоритет ресурсов по умолчанию:
<img src="hero.jpg" fetchpriority="high">— hero-изображение загружается раньше прочих.<script src="analytics.js" fetchpriority="low">— некритический JS откладывается ниже CSS и шрифтов.
Core Web Vitals
Core Web Vitals от Google количественно оценивают воспринимаемую производительность. Ранжирование в поиске использует полевые данные RUM (измерения реальных пользователей через Chrome UX Report), а не синтетические оценки Lighthouse.
LCP — Largest Contentful Paint. Когда самый большой видимый элемент завершает рендеринг. Обычно это hero-изображение или основной заголовок. Хорошо: менее 2,5 с. Плохо: более 4 с. Основные причины плохого LCP: медленный TTFB сервера, ресурсы, блокирующие рендеринг и задерживающие главное изображение, большие неоптимизированные файлы изображений.
INP — Interaction to Next Paint (заменил FID в марте 2024). Наибольшая задержка между любым вводом пользователя (клик, касание, нажатие клавиши) и результирующим визуальным обновлением на протяжении всего жизненного цикла страницы. Хорошо: менее 200 мс. Плохо: более 500 мс. Основная причина: долгие задачи в главном потоке, блокирующие UI.
CLS — Cumulative Layout Shift. Неожиданное смещение видимых элементов во время загрузки страницы. Оценка: сумма (доля воздействия × доля расстояния) для каждого неожиданного смещения. Хорошо: менее 0,1. Основные причины: изображения без объявленных размеров, реклама, вставляющая контент, перетекание текста из-за веб-шрифтов.
Почему это работает
Google использует полевые данные RUM (измерения реальных пользователей из Chrome UX Report) для ранжирования, а не Lighthouse. Страница с оценкой 95 в Lighthouse на вашем ноутбуке может получить 55 в CrUX, если реальные пользователи работают на медленных Android-телефонах в слабой сети. Оптимизируйте для поля, а не лаборатории.
Почему CSS по умолчанию блокирует рендеринг?
Проследите, что делает браузер во время потоковой передачи HTML — от первого байта до LCP.
Упорядочьте эти вмешательства по влиянию на LCP (от наибольшего к наименьшему, при условии что LCP — это hero-изображение):
- 1 Кэшировать HTML на CDN edge, чтобы TTFB снизился с 300 мс до 20 мс
- 2 Добавить <link rel=preload as=image> для hero-изображения, чтобы оно загружалось параллельно с CSS
- 3 Конвертировать hero-изображение из JPEG в WebP (файл на 30% меньше)
- 4 Добавить fetchpriority=high к тегу hero <img>
- 5 Убрать блокирующий рендеринг скрипт аналитики из <head> (перенести в defer)
Что измеряет INP, чего не делал FID?
- 01Почему preload scanner критичен для быстрой загрузки страниц и что выводит его из строя?
- 02Что вызывает CLS и как это исправить?
- 03Google ранжирует страницы по полевым данным RUM, а не Lighthouse. Какое практическое значение это имеет?
Критический путь рендеринга браузера преобразует байты в пиксели через шесть этапов: разбор HTML, разбор CSS, построение дерева рендеринга, компоновка, отрисовка и композитинг. CSS блокирует рендеринг — ничего не отрисовывается до готовности CSSOM — делая его ресурсом с наивысшим приоритетом доставки. JavaScript без defer или async блокирует разбор HTML, задерживая всё. Preload scanner спасает, отправляя запросы подресурсов опережая основной парсер, но выводится из строя динамически внедрёнными URL. Core Web Vitals (LCP, INP, CLS) измеряют восприятие пользователем этого конвейера; Google использует полевые данные RUM (реальные пользователи Chrome), а не оценки Lighthouse для ранжирования. Четыре наиболее эффективных улучшения: CDN-кэш HTML (снизить TTFB), предзагрузить hero-изображение, отложить некритический JS и добавить размеры изображениям.
встречается в165
- Путь запроса: семь остановок от сокета до ответаjunior
- Accept и парсинг: от очереди ядра до типизированного запросаmiddle
- Маршрутизация и middleware: что выполняется и в каком порядкеmiddle
- Обработчик и ответ: от бизнес-логики до байтов на проводеmiddle
- Стриминг и backpressure: когда клиент читает медленнее, чем вы пишетеsenior
- Таймауты и хвостовая задержка: бюджеты, дедлайны и ловушка fan-outsenior
- Middleware и DI: два паттерна, формирующие любой backendjunior
- Пишем middleware: сигнатуры, next() и три модели фреймворковmiddle
- Инверсия управления: как зависимости добираются до классаmiddle
- Скоупы и время жизни DI: singleton, request, transientmiddle
- DI как шов для тестов: фейки, моки и граница, которая важнаsenior
- DI-контейнеры в продакшене: графы разрешения, циклы и когда не стоитsenior
- Блокирующий vs неблокирующий I/O: два способа ждатьjunior
- Event loop: один поток, упорядоченные фазыmiddle
- Что блокирует цикл: CPU-работа и синхронные вызовыmiddle
- Вынос CPU-работы: worker threads и пул libuvmiddle
- Backpressure и ограниченная конкурентностьsenior
- Пропускная способность под нагрузкой: хвостовая задержка и насыщениеsenior
- Зачем пул: цена создания соединенияjunior
- Размер пула: почему больше не значит быстрееmiddle
- Взятие и таймауты: очередь ожидания — настоящий дроссель задержкиmiddle
- Стратегии retry: backoff, jitter и thundering herdmiddle
- Наблюдаемость, production-инциденты и дизайн для глобального масштабаsenior
- Задачи, микрозадачи и scheduler.yield()middle
- Точность таймеров, троттлинг и фоновая работаmiddle
- Event loop Node.js: фазы, nextTick и задержка циклаsenior
- Стратегии рендеринга: SSG, SSR, ISR, streaming и гидратацияjunior
- SSG, SSR, ISR, streaming и RSC — как работает каждая стратегияmiddle
- Цена гидратации: selective, progressive, острова, resumabilitymiddle
- Core Web Vitals: что измеряют LCP, INP и CLSjunior
- LCP: четыре фазы, одна доминирующая стоимостьmiddle
- INP: input delay, processing, presentationmiddle
- Lab vs field: почему они расходятся и как использовать каждыйmiddle
- Трейдоффы метрик, RUM-атрибуция и цикл CI+полеsenior
- Общая картина: от URL до LCP до INP как эстафетаjunior
- Восемь слоёв трассировки: от service worker до второй навигацииmiddle
- Пять канонических поломок: где производство стабильно ломаетсяsenior
- Метод трёх треков: чтение трасс и построение системы мониторингаsenior
- Что такое индекс и как он ускоряет запросыjunior
- Leading-column rule: почему порядок столбцов в composite-индексе важенmiddle
- Partial, expression и covering-индексыmiddle
- Типы индексов: GIN, GiST, BRIN, Hash, Bloom и HOT-обновленияmiddle
- Index-only scan, Visibility Map и INCLUDEsenior
- Типичные сбои в продакшне и аудит индексовsenior
- Упражнение по проектированию индексов: стратегия полнотекстового поискаsenior
- EXPLAIN и планы выполнения: что решает планировщик и почемуjunior
- Типы сканирования: Seq, Index, Bitmap, Index-Onlymiddle
- Алгоритмы соединения и каскад ошибок оценки строкmiddle
- pg_statistic, ANALYZE и производственная наблюдаемостьmiddle
- Расширенная статистика: исправление ошибок оценки для коррелированных колонокsenior
- Кеш планов, настройка константных стоимостей и внутренности планировщикаsenior
- Производственные режимы отказа и стабильность плановsenior
- MVCC: как Postgres раздаёт согласованные снимкиjunior
- Connection pool: зачем амортизировать стоимость backend Postgresjunior
- Режимы PgBouncer: session, transaction и statementmiddle
- Размер пула: формула (ядра × 2) + шпинделей и двухуровневый стекmiddle
- Исчерпание пула и idle-in-transaction: сценарий отказа в 3 ночиmiddle
- Миграция на transaction mode: план развёртывания и prepared statements в PgBouncer 1.21middle
- Процессная модель Postgres и почему увеличение max_connections снижает производительностьsenior
- Ландшафт пулеров 2026, serverless connection storms и полная таксономия отказовsenior
- ADD COLUMN: мгновенно в PG 11+ против перезаписи в старом Postgresjunior
- Режим отказа очереди блокировок: почему мгновенный DDL может заморозить базуmiddle
- Безопасные DDL-паттерны: NOT VALID, CONCURRENTLY и исправления небезопасных операцийmiddle
- Таксономия сбоев миграций и дисциплина продакшнаsenior
- Выбор ключа шарда: стратегии hash, range, list и directorymiddle
- Ко-локация и Citus: инвариант, делающий шардирование пригодным к использованиюmiddle
- Режим отказа hot shard: обнаружение, изоляция и долгосрочная политикаmiddle
- Онлайн-решардинг, 2PC и операционная стоимость шардированияsenior
- Семь актов: от CREATE TABLE до Citusjunior
- Акты 1–3 в глубину: схема, индексы и статистика планировщикаmiddle
- Акты 4–6 в глубину: MVCC bloat, connection pooling и безопасные миграцииmiddle
- Акт 7 в глубину: шардинг, co-location и семиуровневый каскад трейдоффовmiddle
- Наблюдаемость, антипаттерны и производственный триажsenior
- Raft в реальном мире: partition, медленный диск и клиентская маршрутизацияmiddle
- Raft в production: membership change, Multi-Raft и observabilitysenior
- Что такое три сигнала: метрики, логи, трейсыjunior
- Метрики и cardinality: cost-модель time-series databasemiddle
- Логи и объём: cost-модель структурного логированияmiddle
- Трейсы и сэмплирование: cost-модель distributed tracingmiddle
- Join-ключи и exemplar''''ы: как три сигнала становятся компонуемымиmiddle
- Observability 2.0: широкие события и сдвиг стоимостиsenior
- Режимы сбоя и инженерная практика: cardinality budget''''ы, PII и сэмплированиеsenior
- Зачем нужны структурные логи: дневник против таблицыjunior
- Схема продакшн-лога: поля, которые несёт каждая строкаmiddle
- Log levels и маршрутизация алертовmiddle
- Стратегии sampling и стоимость логовmiddle
- PII-редакция и log injectionsenior
- Propagation trace-контекста в логахsenior
- OTel Logs Data Model и audit-логи как подсистемаsenior
- Сигналы OTel, Semantic Conventions и проводной формат OTLPmiddle
- Авто-инструментирование и ручные спаны: правило 80/20 в OTelmiddle
- Collector OTel: receivers, processors, exporters и паттерны развёртыванияmiddle
- Стратегии сэмплирования: head, tail и parent-basedmiddle
- Vendor-нейтральность, eBPF-инструментирование, Operator и OTel в браузере и serverlesssenior
- Эксплуатация OTel Collector: надёжность, version skew, режимы отказа и управлениеsenior
- RED и USE: два чек-листа, одна дисциплина триажаjunior
- Инструментация RED в Prometheus: счётчики, гистограммы и дисциплина cardinalitymiddle
- USE на Linux: CPU, память, диск, сеть и PSImiddle
- Golden signals, структура дашборда и auto-RED в service meshmiddle
- Cardinality как драйвер затрат: label, PII, exemplars и семплированиеmiddle
- Native histograms, SLO и паттерны production-сбоевmiddle
- Выбор SLI и SLO-целей: отношения, не ощущенияmiddle
- Multi-window multi-burn-rate-алертинг: почему AND лучше ORmiddle
- Error budget policy, latency SLO и составные journeysmiddle
- Iceberg SLI, математика составного SLO и SLA vs SLOsenior
- Flame graph: читаем картинку, которая показывает, куда ушло времяjunior
- Sampling vs instrumentation profiling: почему 99 Гц побеждает в productionmiddle
- Типы профилей: CPU, память, off-CPU, mutex — какой когда братьmiddle
- Continuous profiling: always-on flame graphs с eBPF и корреляцией trace-idmiddle
- Как flame graph строится из сэмплов и как использовать его в productionmiddle
- Linux perf, внутренности eBPF, PGO и ограничения sampling''''аsenior
- Profiling в production: безопасность, war stories, OTel profiles и дизайн инфраструктурыsenior
- Debugging-воронка: SLO → RED → trace → profilejunior
- Архитектура OTel: один SDK, четыре сигнала, один wire-форматmiddle
- Экономия на observability: удерживаем затраты в пределах 5% inframiddle
- Масштаб, безопасность и ROI наблюдаемых системsenior
- Сначала профиль: измерь куда реально уходит времяjunior
- Закон Амдала и self-time: потолок любого ускорения, которое ты можешь выпуститьmiddle
- Измерительный цикл: микробенч, макробенч, prod-профиль, эффект наблюдателяmiddle
- Чтение флейм-графов: формы, профайлеры по языкам и 60-секундный сканmiddle
- Статистические baseline''''ы: почему один запуск — не измерениеmiddle
- История профайлеров и ловушки микробенчей: от Кнута до GWPsenior
- Hardware counters, профили холодного старта и безопасность профилейsenior
- Непрерывное профилирование в масштабе: затраты, CI-гейты, корреляция с трейсами и антипаттерныsenior
- Что делает путь горячим: симптом против причиныjunior
- Пять форм hotspot''''а: CPU, аллокации, кэш, лок, syscallmiddle
- Чтение parent и child chains: где применять правкуmiddle
- JIT deopt, цикл fix-and-verify и PR-time профилированиеmiddle
- Аппаратные счётчики и Intel TMA: диагностика подкатегорийsenior
- False sharing и горячие пути нативных мостовsenior
- Горячие пути в production: безопасность, хвостовая латентность и происхождение инструментовsenior
- Иерархия памяти: почему расстояние важнее числа операцийjunior
- Row-major vs column-major: порядок доступа и разрыв в 9xjunior
- Branch prediction: 10–30 циклов штрафа за неожиданный ifmiddle
- Hardware prefetcher, TLB и memory-level parallelismsenior
- Основы GC: за что рантайм берёт налогjunior
- Алгоритмы GC: поколенческая гипотеза, concurrent marking и write barriermiddle
- GC tradeoffs: пауза, throughput, память и давление аллокацийmiddle
- Настройка GC: пейсинг, форма кучи и наблюдаемость аллокацийmiddle
- Внутреннее устройство GC: tri-color инвариант, write barriers и глубокое погружение в рантаймыsenior
- GC в production: наблюдаемость, безопасность, edge cases и управление флотомsenior
- N+1: одна логическая операция, много round-trip''''овjunior
- Семейства фиксов: JOIN, IN, preload и DataLoadermiddle
- Обнаружение N+1: query logs, APM traces и CI gatesmiddle
- DataLoader: батчинг по дереву резолверовmiddle
- Кросс-протокольный N+1: HTTP fan-out и Redis MGETmiddle
- N+1 в масштабе: исчерпание пула, изменения планов и денормализацияsenior
- Batching: амортизируй фиксированную цену каждой операцииjunior
- Окно батчинга: размер и время ожиданияmiddle
- Batching в Kafka и Postgresmiddle
- io_uring и наблюдаемость пакетированияmiddle
- От Nagle до io_uring: эволюция пакетированияmiddle
- Backpressure, изоляция сбоев и безопасность батчей в продакшенеsenior
- Что на самом деле стоит bundle: download, parse, compile, executejunior
- Core Web Vitals: LCP, INP и CLSmiddle
- Code splitting: route-level, component-level, vendor splittingmiddle
- Tree shaking и compression: удаляем то, что не используемmiddle
- Third-party scripts: тихий убийца бюджетаmiddle
- CI enforcement и RUM: делаем бюджеты рабочимиmiddle
- V8 JIT-пайплайн, HTTP-приоритеты и безопасность bundlesenior
- Цикл performance: дисциплина, а не проектjunior
- Классификация и исправление: сопоставление family bottleneck с методамиmiddle
- Observability-стек и CI gates: ловить регрессии до выпускаmiddle
- От инцидента к enforcement: SLO burn до верифицированного исправления за 35 минутmiddle
- Культура, экономика и масштаб performancesenior