A7. HTTP-сервер у трьох редакціях
Це найпереконливіша робота курсу, бо результат тут неможливо переказати, його видно на графіку. Одна й та сама задача, три моделі вводу-виводу, однакове навантаження і три різні точки, у яких усе ламається.
Що має вийти
Section titled “Що має вийти”Три сервери з однаковою поведінкою: віддають фіксовану відповідь
на GET /, слухають порт із аргументу.
./srv-threads 8080./srv-epoll 8080./srv-uring 8080Плюс звіт із вимірами й поясненням.
-
Потік на з’єднання.
acceptу циклі, на кожне з’єднанняpthread_create. Найпростіший і найзрозуміліший варіант — саме тому з нього починаємо.Одразу порахуйте: скільки пам’яті займуть стеки на 10 000 з’єднань при вашому
ulimit -s? Це число знадобиться у звіті. -
Пул потоків.
Та сама модель, але потоки не створюються на кожне з’єднання. Заміряйте окремо: частина накладних витрат зникає, а обмеження лишається.
-
epoll.Один потік, неблокуючі сокети, цикл подій. Не забудьте, що
acceptтеж має бути неблокуючим і що дані можуть прийти частинами.Порівняйте з першою версією не лише за пропускною здатністю, а й за пам’яттю на з’єднання.
-
io_uring.Заявки на
accept,recvіsendподаються в кільце, результати забираються з другого. Використайтеliburing, писати сирі системні виклики не обов’язково. -
Вимірювання.
Навантаження створюйте
wrk,abабо власним клієнтом. Обов’язкові чотири точки: 100, 1 000, 5 000 і 10 000 одночасних з’єднань.Для кожної точки фіксуйте: запитів за секунду, затримку p50 і p99,
RSSсервера, кількість контекстних перемикань (/proc/<pid>/status, поляvoluntary_ctxt_switchesіnonvoluntary_ctxt_switches). -
Звіт.
Три графіки: «з’єднання → запити/с», «з’єднання → p99», «з’єднання → пам’ять». І текст: де саме зламалася кожна модель і чому.
Автоперевірка
Section titled “Автоперевірка”Перевірка лежить в архіві з файлами курсу, і команди нижче виконуються з розпакованого каталогу.
cd labs/a7-http-server./check.sh ./srv-epoll 8080Перевірка запускає сервер, надсилає коректні й навмисно биті запити, перевіряє відповіді, тримає повільне з’єднання і контролює, що інші клієнти при цьому обслуговуються.
Часті помилки
Section titled “Часті помилки”Читання одним read. TCP не зобов’язаний віддати запит цілком, тож
потрібен цикл до порожнього рядка.
Дескриптори течуть. Забули close на шляху помилки. Побачити можна
через ls /proc/<pid>/fd | wc -l під навантаженням.
EAGAIN вважають помилкою. На неблокуючому сокеті це нормальна
відповідь «зараз даних немає», а зовсім не збій.
Один повільний клієнт зупиняє всіх. У версії з epoll це означає,
що десь усередині циклу подій ви зробили блокуючий виклик.
Заміри на тій самій машині. Клієнт конкуруватиме з сервером за процесор. Так робити можна, якщо чесно написати про це у звіті, але для абсолютних чисел цей спосіб не годиться.
Далі, якщо цікаво
Section titled “Далі, якщо цікаво”Додати SO_REUSEPORT і кілька процесів із власним epoll у кожному —
модель, яку використовує nginx. Порівняти з одним циклом подій
на всіх ядрах.