Для поиска оставшихся микрозадержек мы применили инструменты продвинутой диагностики — flamegraph и bpftrace.
Flamegraph показывает, на что тратится CPU-время в виде наглядной иерархии вызовов:
# Собираем данные perf за 60 секунд
perf record -F 99 -ag -- sleep 60
# Генерируем flamegraph
git clone https://github.com/brendangregg/FlameGraph.git
perf script | ./FlameGraph/stackcollapse-perf.pl | \
./FlameGraph/flamegraph.pl > flamegraph.svg
Flamegraph выявил, что 8% CPU-времени Django-воркеров уходит на сериализацию JSON через стандартный модуль json. Замена на orjson дала ускорение сериализации в 6 раз.
Bpftrace — для трассировки конкретных системных вызовов:
# Измеряем латентность fsync (критично для PostgreSQL)
bpftrace -e '
tracepoint:syscalls:sys_enter_fsync { @start[tid] = nsecs; }
tracepoint:syscalls:sys_exit_fsync /@start[tid]/ {
@us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}
interval:s:10 { exit(); }
'
# Результат — гистограмма латентности fsync:
# @us:
# [1, 2) 1205 |@@@@@@@@@@@@@@@@@@@@|
# [2, 4) 834 |@@@@@@@@@@@@@@@ |
# [4, 8) 312 |@@@@@ |
# [8, 16) 47 |@ |
# [16, 32) 12 | |
# [32, 64) 3 | | ← выбросы, требуют внимания
# Отслеживаем процессы с наибольшим количеством context switch
bpftrace -e '
tracepoint:sched:sched_switch {
@[comm] = count();
}
interval:s:5 { print(@); clear(@); }
'
Bpftrace показал, что Node.js-процессы, обрабатывающие WebSocket-соединения, делали избыточное количество context switch (85 000/сек) из-за неоптимального event loop. После тюнинга UV_THREADPOOL_SIZE и объединения мелких записей в буфер количество переключений снизилось до 12 000/сек.
Оставить комментарий