як і в
Якось я прокинувся від невдоволеного листа через великі затримки у Елвіна, якого ми планували запустити найближчим часом. Зокрема, клієнт зіткнувся із затримкою 99-го процентиля в районі 50 мс, набагато вищим за наш бюджет затримки. Це було дивно, тому що я ретельно тестував сервіс, особливо на затримки, адже це є предметом частих скарг.
Перш ніж віддати Елвіна в тестування, я провів багато експериментів із 40 тис. запитів за секунду (QPS), всі показали затримку менше 10 мс. Я був готовий заявити, що не згоден з їхніми результатами. Але ще раз глянувши на листа, я звернув увагу на щось нове: я точно не тестував умови, які вони згадали, їх QPS був набагато нижчим, ніж мій. Я тестував на 40k QPS, а вони лише на 1k. Я запустив ще один експеримент, на цей раз з більш низьким QPS, просто щоб ублажити їх.
Оскільки я пишу про це в блозі, мабуть, ви вже зрозуміли: їхні цифри виявилися правильними. Я перевіряв свого віртуального клієнта знову і знову, все з тим самим результатом: низька кількість запитів не тільки збільшує затримку, але й збільшує кількість запитів із затримкою більше 10 мс. Іншими словами, якщо на 40k QPS близько 50 запитів на секунду перевищували 50 мс, то на 1k QPS кожну секунду було 100 запитів вище 50 мс. Парадокс!
Звужаємо коло пошуку
Зіткнувшись із проблемою затримки у розподіленій системі з багатьма компонентами насамперед потрібно скласти короткий список підозрюваних. Копнем трохи глибше в архітектуру Елвіна:
Хорошою відправною точкою є список виконаних переходів введення-виводу (мережевий виклик/пошук по диску і т. д.). Спробуємо з'ясувати де затримка. Крім очевидного введення-виводу з клієнтом, Елвін робить додатковий крок: він звертається до сховища даних. Однак це сховище працює в одному кластері з Елвіном, тому там затримка має бути меншою, ніж із клієнтом. Отже, список підозрюваних:
- Мережевий виклик від клієнта до Елвіна.
- Мережевий виклик від Елвіна до сховища даних.
- Пошук на диску у сховищі даних.
- Мережевий виклик із сховища даних до Елвіна.
- Мережевий виклик від Елвіна до клієнта.
Спробуємо викреслити деякі пункти.
Сховище даних ні до чого
Насамперед я перетворив Елвіна на сервер ping-ping, який не обробляє запити. Отримавши запит, він повертає порожню відповідь. Якщо затримка зменшується, то помилка в реалізації Елвіна чи сховища даних – нічого такого нечуваного. У першому експерименті отримуємо такий графік:
Як бачимо, під час використання сервера ping-ping немає жодних поліпшень. Це означає, що сховище даних не збільшує затримку, а список підозрюваних скорочується вдвічі:
- Мережевий виклик від клієнта до Елвіна.
- Мережевий виклик від Елвіна до клієнта.
Здорово! Список швидко скорочується. Я гадав, що майже з'ясував причину.
gRPC
Зараз саме час уявити вам нового гравця: gRPC
добре оптимізований і широко використовується, я вперше використав його в системі такого масштабу, і я очікував, що моя реалізація буде неоптимальною — м'яко кажучи.
Наявність gRPC
у стеку породило нове питання: може, це моя реалізація чи сам gRPC
Чи викликає проблему затримки? Додаємо до списку нового підозрюваного:
- Клієнт викликає бібліотеку
gRPC
- Бібліотека
gRPC
на клієнті виконує мережевий виклик бібліотекиgRPC
на сервері - Бібліотека
gRPC
звертається до Елвіна (операції немає у випадку сервера ping-pong)
Щоб ви розуміли, як виглядає код, моя реалізація клієнта/Елвіна не сильно відрізняється від клієнт-серверних
Примітка: наведений вище список трохи спрощений, оскільки
gRPC
дає можливість використання власної (шаблонної?) потокової моделі, в якій переплітаються стек виконанняgRPC
та реалізація користувача. Задля простоти дотримуватимемося цієї моделі.
Профільування все виправить
Викресливши сховища даних, я подумав, що майже закінчив: «Тепер легко! Застосуємо профіль і дізнаємось, де виникає затримка». Я
Я взяв чотири профілю: під високим QPS (маленька затримка) та з ping-pong сервером на низькому QPS (велика затримка), як на стороні клієнта, так і на стороні сервера. І просто про всяк випадок також взяв зразок профілю процесора. При порівнянні профілів я зазвичай шукаю аномальний стек дзвінків. Наприклад, на поганій стороні з високою затримкою відбувається набагато більше перемикань контексту (вдесятеро і більше разів). Але в моєму випадку кількість перемикань контексту практично збігалася. На мій страх, там не виявилося нічого істотного.
Додаткове налагодження
Я був у розпачі. Я не знав, які інструменти можна використовувати, а мій наступний план полягав по суті в повторенні експериментів з різними варіаціями, а не чіткому діагностуванні проблеми.
Що якщо
Із самого початку мене непокоїла конкретний час затримки 50 мс. Це дуже багато часу. Я вирішив, що вирізатиму шматки з коду, поки не зможу з'ясувати точно, яка частина викликає цю помилку. Потім був експеримент, який спрацював.
Як завжди, заднім розумом здається, що все було очевидним. Я помістив клієнта на одну машину з Елвіном і відправив запит у localhost
. І збільшення затримки зникло!
Щось було не так із мережею.
Освоюємо навички мережевого інженера
Мушу зізнатися: мої знання мережевих технологій жахливі, особливо з урахуванням того, що я з ними працюю щодня. Але мережа була головним підозрюваним і мені потрібно було навчитися, як її налагоджувати.
На щастя інтернет любить тих, хто хоче вчитися. Поєднання ping та tracert здавалося досить гарним початком для налагодження проблем мережного транспорту.
По-перше, я запустив
Потім я спробував
Таким чином, причиною затримки був не мій код, не реалізація gRPC і мережа. Я вже почав хвилюватися, що ніколи цього не зрозумію.
Тепер на якій ОС ми знаходимося
gRPC
широко використовується в Linux, але для Windows це екзотика. Я вирішив провести експеримент, який спрацював: створив віртуальну машину Linux, скомпілював Елвіна для Linux і розгорнув її.
І ось що вийшло: у ping-pong сервері Linux не було таких затримок, як у аналогічного вузла Windows, хоча джерело даних не відрізнялося. Виявляється, проблема в реалізації gRPC для Windows.
Алгоритм Нейгла
Весь цей час я думав, що мені не вистачає прапора gRPC
. Тепер я зрозумів, що насправді це в gRPC
не вистачає прапор Windows. Я знайшов внутрішню бібліотеку RPC, у якій був упевнений, що вона добре працює для всіх встановлених прапорів
майже готово: я почав видаляти додані прапори по одному, доки не повернулася регресія, тож я зміг точно визначити її причину. Це був сумнозвісний
gRPC
було встановлено цей прапор у реалізації Linux для сокетів TCP, але з Windows. Я це
Висновок
Велику затримку на низькому QPS викликала оптимізація операційної системи. Озираючись назад, профільування не виявило затримку, тому що проводилося в режимі ядра, а не в
Що ж до експерименту localhost, він, мабуть, не стосувався фактичного мережного коду, і алгоритм Нейгла не запустився, тому проблеми із затримкою зникли, коли клієнт звернувся до Елвіна через localhost.
Наступного разу, коли побачите збільшення затримки при зменшенні кількості запитів на секунду, алгоритм Нейгла має бути у списку підозрюваних!
Джерело: habr.com