Транспорт бэкенда
Batching, Compression и Zero-Copy
Kafka обрабатывает 1 триллион сообщений в день в LinkedIn. Не через супер-компьютеры - через стандартное железо. Секрет в layer оптимизаций: батчинг сообщений, zstd сжатие, sendfile() для zero-copy отдачи из лога. Каждый уровень умножает throughput. Понимание этих техник отличает 'работает' от 'работает на масштабе'.
- **Kafka** использует sendfile() (Java NIO transferTo) для consumer replay. Это позволяет отдавать данные из disk log в сеть без аллокаций в JVM heap - ключ к 1M+ messages/sec на одном брокере.
- **Cloudflare** использует XDP/eBPF для DDoS mitigation: обрабатывает 26 миллионов пакетов в секунду на одном ядре CPU, блокируя атаку до kernel network stack.
- **AWS** ML training кластеры используют EFA (RDMA) для gradient synchronization между GPU: latency 1-2 мкс вместо 50 мкс TCP - критично при обучении LLM на тысячах GPU.
Batching и Linger
Batching - отправка нескольких сообщений в одном сетевом пакете вместо отдельного вызова на каждое. Overhead на установку соединения и системный вызов amortize через больший батч. Kafka producer по умолчанию батчит с linger.ms=0 (отправить немедленно) - для throughput нужно linger.ms=5-20.
Nagle's algorithm (TCP_NODELAY=false) - это batching на уровне TCP: ждём пока накопится данных или придёт ACK. Для low-latency приложений (игры, финансы) устанавливают TCP_NODELAY=true чтобы отключить Nagle и отправлять немедленно.
Kafka producer с linger.ms=20 vs linger.ms=0. Что изменится?
Compression: алгоритмы и trade-offs
Сжатие данных при передаче уменьшает сетевой трафик за счёт CPU. Trade-off зависит от CPU bound vs network bound ситуации и типа данных. JSON сжимается в 5-10x, бинарные данные - в 2-3x.
Facebook перешёл с zlib на zstd в 2016 году: 2.5x быстрее сжатие при том же ratio. Meta использует zstd для Thrift-сообщений между сервисами. AWS добавил zstd поддержку в S3 и CloudFront в 2023.
Kafka сервис передаёт JSON события (~10KB каждое). Какой алгоритм сжатия предпочтителен для high-throughput?
Zero-Copy и sendfile()
Обычная передача файла копирует данные 4 раза: disk -> kernel buffer -> user buffer -> socket buffer -> NIC. Zero-Copy (sendfile syscall) устраняет копию через user space: disk -> kernel buffer -> NIC напрямую. Kafka использует sendfile для replay из лога - это ключ к его throughput.
Linux splice() - более гибкий вариант sendfile: перемещает данные между pipe file descriptors. mmap() + write() - альтернатива: mapирует файл в адресное пространство процесса, избегая одну из копий. Java NIO MappedByteBuffer использует mmap.
Почему Kafka критически зависит от sendfile() для высокого throughput?
io_uring: асинхронный I/O
io_uring (Linux 5.1, 2019) - новый асинхронный I/O interface, устраняющий overhead системных вызовов через разделяемые кольцевые буферы между ядром и пространством пользователя. Zero system call path для hot paths. Использует submission ring + completion ring.
io_uring поддерживает не только I/O: accept(), connect(), sendmsg(), splice(), fsync() - всё через ту же кольцевую очередь. Это создаёт unified async interface для сетевых и файловых операций. Автор: Jens Axboe (также создатель blktrace, fio).
В чём главное архитектурное преимущество io_uring над epoll?
Kernel Bypass: DPDK и RDMA
Kernel bypass - технологии, позволяющие приложению работать с сетью или памятью напрямую, минуя ядро ОС. DPDK (Data Plane Development Kit) - user-space polling сетевого адаптера. RDMA (Remote Direct Memory Access) - прямой доступ к памяти другой машины без CPU участия.
AWS Elastic Fabric Adapter (EFA) предоставляет RDMA-подобную коммуникацию для HPC и ML workloads. NVIDIA NVLink для GPU-to-GPU: 600 GB/s bandwidth без CPU overhead - используется в A100, H100 кластерах для LLM training.
Compression всегда улучшает производительность - нужно включать везде
Compression - trade-off: CPU vs сеть. Для CPU-bound сервисов или уже сжатых данных (JPEG, MP4) compression ухудшит производительность.
Streaming видео: nginx отдаёт mp4 файлы. Включить gzip? Нет - MP4 уже сжат, gzip потратит CPU без уменьшения size. sendfile без compression в этом случае оптимален.
Для какого типа workload оправдан DPDK (kernel bypass)?
Итоги
- **Batching** amortizes syscall и network overhead: linger.ms в Kafka, bufferTime в RxJS. Trade-off: latency растёт, throughput растёт.
- **Compression** выгодна для текстовых данных (JSON, protobuf) на network-bound сервисах. lz4/zstd для real-time, brotli/gzip для HTTP. Не сжимать уже сжатое (JPEG, MP4).
- **Zero-Copy** через sendfile() устраняет user-space копирование: disk -> kernel -> NIC без JVM heap. io_uring идёт дальше: устраняет syscall overhead через shared rings.
Связанные темы
Batching и zero-copy оптимизации работают на всех уровнях транспортного стека:
- Kafka: producer и consumer — Kafka батчинг (batch.size, linger.ms) и sendfile для consumer - практические применения этих техник
- Бенчмаркинг транспорта — Измерить эффект batching и compression можно только через правильный бенчмаркинг с P99 latency и throughput метриками
Вопросы для размышления
- Как определить оптимальный linger.ms для Kafka producer в конкретном приложении?
- В каких случаях io_uring не даёт преимущества над epoll?
- Как batching влияет на latency percentiles (P50 vs P99) по-разному?