Както в
Един ден се събудих с недоволен имейл поради големи закъснения с Алвин, който планирахме да стартираме в близко бъдеще. По-конкретно, клиентът изпита 99-ти процентил на латентност в района на 50 ms, доста над нашия бюджет за латентност. Това беше изненадващо, тъй като тествах широко услугата, особено по отношение на латентността, което е често срещано оплакване.
Преди да подложа Alvin на тестване, проведох много експерименти с 40 10 заявки в секунда (QPS), всички показващи латентност от по-малко от 40 ms. Бях готов да заявя, че не съм съгласен с техните резултати. Но като погледнах отново писмото, забелязах нещо ново: не бях тествал точно условията, които споменаха, техните QPS бяха много по-ниски от моите. Тествах на 1k QPS, но те само на XNUMXk. Проведох друг експеримент, този път с по-нисък QPS, само за да ги успокоя.
Тъй като пиша в блог за това, вероятно вече сте разбрали, че техните числа са правилни. Тествах моя виртуален клиент отново и отново със същия резултат: малък брой заявки не само увеличава латентността, но увеличава броя на заявките с латентност над 10 ms. С други думи, ако при 40k QPS около 50 заявки в секунда надвишават 50 ms, тогава при 1k QPS има 100 заявки над 50 ms всяка секунда. Парадокс!
Стесняване на търсенето
Когато се сблъскате с проблем със закъснението в разпределена система с много компоненти, първата стъпка е да създадете кратък списък от заподозрени. Нека се впуснем малко по-дълбоко в архитектурата на Алвин:
Добра отправна точка е списък със завършени I/O преходи (мрежови повиквания/търсене на диск и т.н.). Нека се опитаме да разберем къде е забавянето. Освен очевидния I/O с клиента, Алвин предприема допълнителна стъпка: той осъществява достъп до хранилището на данни. Това хранилище обаче работи в същия клъстер като Alvin, така че забавянето там трябва да е по-малко, отколкото при клиента. И така, списъкът на заподозрените:
- Мрежово обаждане от клиент до Алвин.
- Мрежово обаждане от Alvin към хранилището на данни.
- Търсене на диск в хранилището на данни.
- Мрежово обаждане от хранилището на данни до Алвин.
- Мрежово обаждане от Алвин до клиент.
Нека се опитаме да зачеркнем някои точки.
Съхранението на данни няма нищо общо с това
Първото нещо, което направих, беше да конвертирам Alvin в ping-ping сървър, който не обработва заявки. Когато получи заявка, тя връща празен отговор. Ако забавянето намалее, тогава грешка в изпълнението на Alvin или хранилище за данни не е нещо нечувано. В първия експеримент получаваме следната графика:
Както можете да видите, няма подобрение при използване на ping-ping сървъра. Това означава, че хранилището на данни не увеличава латентността и списъкът на заподозрените е намален наполовина:
- Мрежово обаждане от клиент до Алвин.
- Мрежово обаждане от Алвин до клиент.
Страхотен! Списъкът бързо намалява. Мислех, че почти разбрах причината.
gRPC
Сега е моментът да ви представим нов играч: gRPC
добре оптимизиран и широко използван, това беше първият ми път, когато го използвах на система с такъв размер и очаквах внедряването ми да бъде неоптимално - най-малкото.
наличност gRPC
в стека породи нов въпрос: може би това е моята реализация или аз gRPC
причинява проблем със закъснението? Добавяне на нов заподозрян към списъка:
- Клиентът се обажда в библиотеката
gRPC
- Библиотека
gRPC
прави мрежово повикване към библиотеката на клиентаgRPC
на сървъра - Библиотека
gRPC
контакти с Алвин (няма операция в случай на пинг-понг сървър)
За да ви дам представа как изглежда кодът, моята реализация клиент/Алвин не се различава много от тези клиент-сървър
Забележка: Горният списък е малко опростен, защото
gRPC
прави възможно използването на ваш собствен (шаблон?) нишков модел, в който стекът за изпълнение е преплетенgRPC
и потребителско внедряване. За по-голяма простота ще се спрем на този модел.
Профилирането ще оправи всичко
След като зачеркнах хранилищата за данни, си помислих, че почти съм готов: „Сега е лесно! Нека приложим профила и да разберем къде възниква забавянето.“ аз
Взех четири профила: с висок QPS (ниска латентност) и с пинг-понг сървър с нисък QPS (висока латентност), както от страна на клиента, така и от страна на сървъра. И за всеки случай взех и примерен профил на процесора. Когато сравнявам профили, обикновено търся аномален стек от повиквания. Например, лошата страна с висока латентност има много повече контекстни превключвания (10 пъти или повече). Но в моя случай броят на контекстните превключватели беше почти същият. За мой ужас там нямаше нищо съществено.
Допълнително отстраняване на грешки
Бях отчаян. Не знаех какви други инструменти мога да използвам и следващият ми план беше по същество да повторя експериментите с различни вариации, вместо ясно да диагностицирам проблема.
Какво ако
От самото начало бях загрижен за специфичната латентност от 50 ms. Това е много голямо време. Реших, че ще изрежа части от кода, докато мога да разбера коя точно част причинява тази грешка. Тогава дойде експеримент, който проработи.
Както обикновено, погледнато назад изглежда, че всичко е очевидно. Поставих клиента на същата машина като Алвин - и изпратих заявка до localhost
. И увеличението на латентността изчезна!
Нещо не беше наред с мрежата.
Учене на умения за мрежов инженер
Трябва да призная: познанията ми по мрежовите технологии са ужасни, особено предвид факта, че работя с тях всеки ден. Но мрежата беше главният заподозрян и трябваше да се науча как да отстранявам грешки.
За щастие, Интернет обича онези, които искат да учат. Комбинацията от ping и tracert изглеждаше като достатъчно добро начало за отстраняване на грешки при проблеми с мрежовия транспорт.
Първо, стартирах
Тогава опитах
Така че не моят код, внедряването на gRPC или мрежата са причинили забавянето. Започвах да се притеснявам, че никога няма да разбера това.
Сега на каква ОС сме
gRPC
широко използван в Linux, но екзотичен в Windows. Реших да опитам експеримент, който проработи: създадох виртуална машина на Linux, компилирах Alvin за Linux и я внедрих.
И ето какво се случи: сървърът за пинг-понг на Linux нямаше същите забавяния като подобен хост на Windows, въпреки че източникът на данни не беше по-различен. Оказва се, че проблемът е в имплементацията на gRPC за Windows.
Алгоритъмът на Nagle
През цялото това време си мислех, че ми липсва знаме gRPC
. Сега разбирам какво е всъщност gRPC
Windows флаг липсва. Намерих вътрешна RPC библиотека, за която бях уверен, че ще работи добре за всички зададени флагове
почти Готово: Започнах да премахвам добавените флагове един по един, докато регресията се върна, за да мога да определя причината. Беше позорно
gRPC
този флаг е зададен в изпълнението на Linux за TCP сокети, но не и в Windows. аз съм това
Заключение
По-високата латентност при ниски QPS е причинена от оптимизация на ОС. В ретроспекция профилирането не откри латентност, защото беше направено в режим на ядрото, а не в
Що се отнася до експеримента с localhost, той вероятно не е докоснал действителния мрежов код и алгоритъмът на Nagle не е работил, така че проблемите със закъснението са изчезнали, когато клиентът достигне до Alvin чрез localhost.
Следващият път, когато видите увеличаване на латентността, тъй като броят на заявките в секунда намалява, алгоритъмът на Nagle трябва да бъде в списъка ви със заподозрени!
Източник: www.habr.com