Como dentro
Un día espertei cun correo electrónico descontento debido aos longos atrasos con Alvin, que planeabamos lanzar nun futuro próximo. En concreto, o cliente experimentou unha latencia do percentil 99 na rexión de 50 ms, moi por riba do noso orzamento de latencia. Isto foi sorprendente xa que probei o servizo extensamente, especialmente na latencia, que é unha queixa común.
Antes de probar a Alvin, realicei moitos experimentos con 40 consultas por segundo (QPS), todos mostrando unha latencia inferior a 10 ms. Estaba preparado para declarar que non estaba de acordo cos seus resultados. Pero botando unha nova ollada á carta, notei algo novo: non probara exactamente as condicións que mencionaban, o seu QPS era moito máis baixo que o meu. Probei a 40k QPS, pero só a 1k. Fixen outro experimento, esta vez cun QPS máis baixo, só para calmalos.
Xa que estou blogueando sobre isto, probablemente xa te decataches de que os seus números eran correctos. Probei o meu cliente virtual unha e outra vez, co mesmo resultado: un número baixo de solicitudes non só aumenta a latencia, senón que aumenta o número de solicitudes cunha latencia superior a 10 ms. Noutras palabras, se a 40k QPS unhas 50 solicitudes por segundo superaban os 50 ms, entón a 1k QPS había 100 solicitudes superiores a 50 ms cada segundo. Paradoxo!
Reducindo a busca
Cando se enfronta a un problema de latencia nun sistema distribuído con moitos compoñentes, o primeiro paso é crear unha pequena lista de sospeitosos. Afondemos un pouco máis na arquitectura de Alvin:
Un bo punto de partida é unha lista de transicións de E/S completadas (chamadas de rede/buscas de discos, etc.). Imos tentar descubrir onde está o atraso. Ademais da evidente E/S co cliente, Alvin dá un paso extra: accede ao almacén de datos. Non obstante, este almacenamento funciona no mesmo clúster que Alvin, polo que a latencia debería ser inferior á do cliente. Entón, a lista de sospeitosos:
- Chamada de rede do cliente a Alvin.
- Chamada de rede de Alvin ao almacén de datos.
- Busca no disco no almacén de datos.
- Chamada de rede do almacén de datos a Alvin.
- Chamada de rede de Alvin a un cliente.
Intentemos tachar algúns puntos.
O almacenamento de datos non ten nada que ver con iso
O primeiro que fixen foi converter Alvin nun servidor de ping-ping que non procesa solicitudes. Cando recibe unha solicitude, devolve unha resposta baleira. Se a latencia diminúe, entón un erro na implementación de Alvin ou do almacén de datos non é nada inaudito. No primeiro experimento obtemos a seguinte gráfica:
Como podes ver, non hai ningunha mellora ao usar o servidor ping-ping. Isto significa que o almacén de datos non aumenta a latencia e a lista de sospeitosos redúcese á metade:
- Chamada de rede do cliente a Alvin.
- Chamada de rede de Alvin a un cliente.
Genial! A lista vaise reducindo rapidamente. Pensei que case descubrira o motivo.
gRPC
Agora é o momento de presentarche un novo xogador: gRPC
ben optimizado e moi utilizado, esta era a primeira vez que o usaba nun sistema deste tamaño e esperaba que a miña implementación fose subóptima, cando menos.
dispoñibilidade gRPC
na pila deu lugar a unha nova pregunta: quizais sexa a miña implementación ou eu mesmo gRPC
está causando un problema de latencia? Engadindo un novo sospeitoso á lista:
- O cliente chama á biblioteca
gRPC
- biblioteca
gRPC
fai unha chamada de rede á biblioteca do clientegRPC
no servidor - biblioteca
gRPC
contactos con Alvin (sen operación en caso de servidor de ping-pong)
Para que che fagas unha idea de como é o código, a miña implementación cliente/Alvin non é moi diferente á cliente-servidor
Nota: a lista anterior está un pouco simplificada porque
gRPC
fai posible utilizar o seu propio modelo de threading (modelo?), no que a pila de execución está entrelazadagRPC
e implementación de usuarios. En aras da sinxeleza, seguiremos este modelo.
A creación de perfiles arranxará todo
Despois de tachar os almacéns de datos, pensei que estaba case rematado: "Agora é fácil! Apliquemos o perfil e descubramos onde se produce o atraso". eu
Tomei catro perfís: con QPS alto (baixa latencia) e cun servidor de ping-pong con QPS baixo (alta latencia), tanto no lado do cliente como no do servidor. E por se acaso, tamén tomei un perfil de procesador de mostra. Ao comparar perfís, adoito buscar unha pila de chamadas anómala. Por exemplo, no lado malo con alta latencia hai moitos máis cambios de contexto (10 veces ou máis). Pero no meu caso, o número de cambios de contexto era case o mesmo. Para o meu horror, non había nada significativo alí.
Depuración adicional
Estaba desesperado. Non sabía que outras ferramentas podía usar, e o meu seguinte plan era esencialmente repetir os experimentos con diferentes variacións en lugar de diagnosticar claramente o problema.
E se
Desde o principio, preocupoume a latencia específica de 50 ms. Este é un momento moi grande. Decidín cortar anacos do código ata que puidese descubrir exactamente que parte estaba a causar este erro. Despois veu un experimento que funcionou.
Como é habitual, coa retrospectiva parece que todo era obvio. Coloquei o cliente na mesma máquina que Alvin e enviei unha solicitude a localhost
. E o aumento da latencia desapareceu!
Produciuse un erro coa rede.
Adquirir habilidades de enxeñeiro de redes
Debo recoñecer: o meu coñecemento das tecnoloxías de rede é terrible, sobre todo tendo en conta o feito de que traballo con elas todos os días. Pero a rede era o principal sospeitoso e necesitaba aprender a depurala.
Afortunadamente, Internet adora os que queren aprender. A combinación de ping e tracert parecía un bo comezo para depurar problemas de transporte de rede.
En primeiro lugar, lancei
Despois tenteino
Polo tanto, non foi o meu código, a implementación de gRPC ou a rede o que estaba a causar o atraso. Comezaba a preocuparme de que nunca entendería isto.
Agora en que sistema operativo estamos
gRPC
moi usado en Linux, pero exótico en Windows. Decidín probar un experimento, que funcionou: creei unha máquina virtual Linux, compilei Alvin para Linux e despregueino.
E aquí está o que pasou: o servidor de ping-pong Linux non tivo os mesmos atrasos que un servidor similar de Windows, aínda que a fonte de datos non era diferente. Resulta que o problema está na implementación de gRPC para Windows.
Algoritmo de Nagle
Durante todo este tempo pensei que me faltaba unha bandeira gRPC
. Agora entendo o que é realmente gRPC
Falta a bandeira de Windows. Atopei unha biblioteca RPC interna na que estaba seguro de que funcionaría ben para todas as marcas establecidas
Case Feito: comecei a eliminar as bandeiras engadidas unha a unha ata que volveu a regresión para poder identificar a causa. Foi infame
gRPC
esta marca estableceuse na implementación de Linux para sockets TCP, pero non en Windows. Eu son isto
Conclusión
A maior latencia a baixo QPS foi causada pola optimización do SO. En retrospectiva, a creación de perfiles non detectou a latencia porque se fixo no modo de núcleo en lugar de en
En canto ao experimento localhost, probablemente non tocou o código de rede real e o algoritmo de Nagle non se executou, polo que os problemas de latencia desapareceron cando o cliente chegou a Alvin a través de localhost.
A próxima vez que vexas un aumento da latencia a medida que diminúe o número de solicitudes por segundo, o algoritmo de Nagle debería estar na túa lista de sospeitosos.
Fonte: www.habr.com