Bazen daha fazlası daha azdır. Yükün azaltılması gecikmenin artmasına neden olur

Gibi çoğu gönderiDağıtılmış bir hizmette sorun var, bu hizmete Alvin adını verelim. Bu sefer sorunu kendim keşfetmedim, müşteri tarafındaki adamlar bana bilgi verdi.

Yakın gelecekte piyasaya sürmeyi planladığımız Alvin ile uzun gecikmeler nedeniyle bir gün canımı sıkan bir e-postayla uyandım. Özellikle müşteri, gecikme bütçemizin çok üzerinde, 99 ms civarında yüzde 50'luk bir gecikme yaşadı. Hizmeti kapsamlı bir şekilde, özellikle de yaygın bir şikayet olan gecikme konusunda test ettiğim için bu şaşırtıcıydı.

Alvin'i teste sokmadan önce, saniyede 40 sorgu (QPS) ile çok sayıda deney yaptım ve bunların tümü 10 ms'den daha az gecikme gösterdi. Sonuçlarına katılmadığımı beyan etmeye hazırdım. Ancak mektuba tekrar baktığımda yeni bir şey fark ettim: Bahsettikleri koşulları tam olarak test etmemiştim, QPS'leri benimkinden çok daha düşüktü. 40k QPS'de test ettim, ancak onlar yalnızca 1k'de. Sırf onları yatıştırmak için bu sefer daha düşük bir QPS ile başka bir deney yaptım.

Bu konuda blog yazdığıma göre muhtemelen sayıların doğru olduğunu zaten anlamışsınızdır. Sanal istemcimi tekrar tekrar test ettim ve aynı sonuçla karşılaştım: isteklerin sayısının az olması yalnızca gecikmeyi artırmakla kalmıyor, aynı zamanda gecikmesi 10 ms'den fazla olan isteklerin sayısını da artırıyor. Başka bir deyişle, 40k QPS'de saniyede yaklaşık 50 istek 50 ms'yi aşarsa, 1k QPS'te saniyede 100 ms'nin üzerinde 50 istek vardı. Paradoks!

Bazen daha fazlası daha azdır. Yükün azaltılması gecikmenin artmasına neden olur

Aramayı daraltma

Pek çok bileşene sahip dağıtılmış bir sistemde gecikme sorunuyla karşılaşıldığında ilk adım, kısa bir şüpheli listesi oluşturmaktır. Alvin'in mimarisine biraz daha derinlemesine bakalım:

Bazen daha fazlası daha azdır. Yükün azaltılması gecikmenin artmasına neden olur

İyi bir başlangıç ​​noktası, tamamlanan G/Ç geçişlerinin (ağ çağrıları/disk aramaları vb.) listesidir. Gecikmenin nerede olduğunu bulmaya çalışalım. Alvin, istemciyle olan bariz G/Ç'nin yanı sıra fazladan bir adım daha atıyor: veri deposuna erişiyor. Ancak bu depolama Alvin ile aynı kümede çalıştığından gecikmenin istemcidekinden daha az olması gerekir. Yani, şüphelilerin listesi:

  1. İstemciden Alvin'e ağ çağrısı.
  2. Alvin'den veri deposuna ağ çağrısı.
  3. Veri deposundaki diskte arama yapın.
  4. Veri ambarından Alvin'e ağ çağrısı.
  5. Alvin'den bir müşteriye ağ çağrısı.

Bazı noktaların üzerini çizmeye çalışalım.

Veri depolamanın bununla hiçbir ilgisi yok

Yaptığım ilk şey Alvin'i istekleri işlemeyen bir ping-ping sunucusuna dönüştürmek oldu. Bir istek aldığında boş bir yanıt döndürür. Gecikme azalırsa Alvin'de veya veri ambarı uygulamasında bir hata duyulmamış bir şey değildir. İlk deneyde aşağıdaki grafiği elde ederiz:

Bazen daha fazlası daha azdır. Yükün azaltılması gecikmenin artmasına neden olur

Gördüğünüz gibi ping-ping sunucusunu kullanırken herhangi bir gelişme yok. Bu, veri ambarının gecikmeyi artırmadığı ve şüpheli listesinin yarıya indirildiği anlamına gelir:

  1. İstemciden Alvin'e ağ çağrısı.
  2. Alvin'den bir müşteriye ağ çağrısı.

Harika! Liste hızla daralıyor. Neredeyse sebebini bulduğumu sanıyordum.

gRPC

Şimdi sizi yeni bir oyuncuyla tanıştırmanın zamanı geldi: gRPC. Bu, süreç içi iletişim için Google'ın açık kaynak kitaplığıdır RPC. Rağmen gRPC iyi optimize edilmiş ve yaygın olarak kullanılmış, bu boyuttaki bir sistemde ilk kez kullanıyordum ve uygulamamın en hafif tabirle optimalin altında olmasını bekliyordum.

kullanılabilirlik gRPC yığında yeni bir soru ortaya çıktı: belki bu benim uygulamamdır ya da kendimdir gRPC gecikme sorununa neden oluyor mu? Listeye yeni bir şüpheli ekleniyor:

  1. Müşteri kütüphaneyi arar gRPC
  2. Kütüphane gRPC istemcideki kütüphaneye ağ çağrısı yapar gRPC sunucuda
  3. Kütüphane gRPC Alvin ile iletişim kurar (ping-pong sunucusu durumunda işlem yapılmaz)

Kodun neye benzediğine dair bir fikir vermek gerekirse, müşteri/Alvin uygulamam istemci-sunucu uygulamalarından pek farklı değil eşzamansız örnekler.

Not: Yukarıdaki liste biraz basitleştirilmiştir çünkü gRPC yürütme yığınının iç içe geçtiği kendi (şablon?) iş parçacığı modelinizi kullanmanızı mümkün kılar gRPC ve kullanıcı uygulaması. Basitlik adına bu modele bağlı kalacağız.

Profil oluşturma her şeyi düzeltecektir

Veri depolarının üzerini çizdikten sonra neredeyse işimin bittiğini düşündüm: “Artık çok kolay! Profili uygulayalım ve gecikmenin nerede oluştuğunu bulalım.” BEN hassas profil oluşturmanın büyük hayranı, çünkü CPU'lar çok hızlıdır ve çoğunlukla darboğaz oluşturmazlar. Çoğu gecikme, işlemcinin başka bir şey yapmak için işlemeyi durdurması gerektiğinde meydana gelir. Doğru CPU Profili Oluşturma tam da bunu yapar: her şeyi doğru bir şekilde kaydeder bağlam anahtarları ve gecikmelerin nerede meydana geldiğini açıkça belirtir.

Dört profil aldım: hem istemci tarafında hem de sunucu tarafında yüksek QPS'li (düşük gecikmeli) ve düşük QPS'li (yüksek gecikmeli) bir ping-pong sunucusuyla. Her ihtimale karşı örnek bir işlemci profili de aldım. Profilleri karşılaştırırken genellikle anormal bir çağrı yığını ararım. Örneğin, yüksek gecikme süresine sahip kötü tarafta çok daha fazla bağlam anahtarı vardır (10 kat veya daha fazla). Ancak benim durumumda bağlam anahtarlarının sayısı neredeyse aynıydı. Beni dehşete düşüren, orada önemli bir şey yoktu.

Ek Hata Ayıklama

Çaresizdim. Başka hangi araçları kullanabileceğimi bilmiyordum ve bir sonraki planım, sorunu açıkça teşhis etmek yerine esas olarak farklı varyasyonlarla deneyleri tekrarlamaktı.

Farzedelim

En başından beri, 50 ms'lik gecikme konusunda endişeliydim. Bu çok büyük bir zaman. Bu hataya tam olarak hangi parçanın neden olduğunu bulana kadar kodun bazı kısımlarını kesmeye karar verdim. Daha sonra işe yarayan bir deney geldi.

Her zamanki gibi geriye dönüp baktığımızda her şeyin apaçık olduğu görülüyor. İstemciyi Alvin ile aynı makineye yerleştirdim ve ona bir istek gönderdim. localhost. Ve gecikmedeki artış ortadan kalktı!

Bazen daha fazlası daha azdır. Yükün azaltılması gecikmenin artmasına neden olur

Ağda bir sorun vardı.

Ağ mühendisi becerilerini öğrenme

İtiraf etmeliyim ki ağ teknolojileri konusundaki bilgim berbat, özellikle de onlarla her gün çalıştığım gerçeği göz önüne alındığında. Ancak asıl şüpheli ağdı ve benim de ağda nasıl hata ayıklayacağımı öğrenmem gerekiyordu.

Neyse ki internet öğrenmek isteyenleri seviyor. Ping ve tracert kombinasyonu, ağ aktarım sorunlarının hatalarını ayıklamak için yeterince iyi bir başlangıç ​​gibi görünüyordu.

Öncelikle başlattım PsPing Alvin'in TCP bağlantı noktasına. Varsayılan ayarları kullandım - özel bir şey yok. Binden fazla pingin hiçbiri 10 ms'yi aşmadı, ilki ısınma içindi. Bu, 50. yüzdelik dilimde gözlemlenen 99 ms'lik gecikme artışına aykırıdır: burada, her 100 istek için, 50 ms'lik gecikmeye sahip yaklaşık bir istek görmemiz gerekirdi.

Sonra denedim tracert: Alvin ile istemci arasındaki rota üzerindeki düğümlerden birinde sorun olabilir. Ancak izleyici de eli boş döndü.

Yani gecikmeye neden olan benim kodum, gRPC uygulaması veya ağ değildi. Bunu hiçbir zaman anlayamayacağım diye endişelenmeye başlamıştım.

Şimdi hangi işletim sistemi üzerindeyiz

gRPC Linux'ta yaygın olarak kullanılır, ancak Windows'ta egzotiktir. İşe yarayan bir deney yapmaya karar verdim: Bir Linux sanal makinesi oluşturdum, Alvin'i Linux için derledim ve dağıttım.

Bazen daha fazlası daha azdır. Yükün azaltılması gecikmenin artmasına neden olur

Ve şöyle oldu: Veri kaynağı farklı olmasa da Linux pinpon sunucusu benzer bir Windows ana bilgisayarıyla aynı gecikmelere sahip değildi. Sorunun Windows için gRPC uygulamasında olduğu ortaya çıktı.

Nagle'ın algoritması

Bunca zaman bir bayrağı kaçırdığımı sanıyordum gRPC. Şimdi gerçekte ne olduğunu anlıyorum gRPC Windows bayrağı eksik. Tüm bayraklar kümesinde işe yarayacağından emin olduğum dahili bir RPC kitaplığı buldum Winsock. Daha sonra tüm bu bayrakları gRPC'ye ekledim ve Alvin'i yamalı bir Windows pinpon sunucusunda Windows'a yerleştirdim!

Bazen daha fazlası daha azdır. Yükün azaltılması gecikmenin artmasına neden olur

Neredeyse Bitti: Sebebini tam olarak belirleyebilmek için, gerileme dönene kadar eklenen işaretleri birer birer kaldırmaya başladım. Rezil bir şeydi TCP_NODELAY, Nagle'ın algoritma anahtarı.

Nagle'ın algoritması Paket boyutu belirli bir bayt sayısını aşıncaya kadar mesajların iletimini geciktirerek ağ üzerinden gönderilen paket sayısını azaltmaya çalışır. Bu, ortalama bir kullanıcı için iyi olsa da, gerçek zamanlı sunucular için yıkıcıdır çünkü işletim sistemi bazı mesajları geciktirerek düşük QPS'de gecikmelere neden olur. sen gRPC bu bayrak, TCP yuvaları için Linux uygulamasında ayarlandı, ancak Windows'ta ayarlanmadı. ben buyum düzeltilmiş.

Sonuç

Düşük QPS'deki yüksek gecikmenin nedeni işletim sistemi optimizasyonudur. Geçmişe bakıldığında, profil oluşturma işlemi çekirdek modunda yapıldığı için gecikmeyi tespit edemedi. Kullanıcı modu. Nagle'ın algoritmasının ETW yakalamaları yoluyla gözlemlenip gözlemlenemeyeceğini bilmiyorum ama ilginç olurdu.

Localhost deneyine gelince, muhtemelen gerçek ağ koduna dokunmadı ve Nagle'nin algoritması çalışmadı, dolayısıyla müşteri localhost aracılığıyla Alvin'e ulaştığında gecikme sorunları ortadan kalktı.

Bir dahaki sefere saniye başına istek sayısı azaldıkça gecikmede bir artış gördüğünüzde, Nagle'ın algoritması şüpheliler listenizde olmalıdır!

Kaynak: habr.com

Yorum ekle