SORU
24 NİSAN 2013, ÇARŞAMBA


Zaman uyumsuz gelen HttpClient .4.5 yoğun yük uygulamaları için kötü bir seçim Net?

Geçenlerde zaman uyumsuz bir şekilde vs klasik birden çok iş parçacığı bir yaklaşım oluşturulabilir HTTP arayın verimliliğini test etmek için basit bir uygulama yarattı.

Uygulama HTTP çağrıları önceden tanımlanmış bir dizi gerçekleştirebilir ve sonunda toplam zaman onları gerçekleştirmek için gereken görüntüler. Benim testler sırasında, tüm HTTP aramalar yerel IIS benim kesmek için yapılmış ve küçük bir metin dosyası (boyutu 12 bayt) getirdiler.

Asenkron uygulama için kodun en önemli kısmı aşağıda listelenmiştir:

public async void TestAsync()
{
    this.TestInit();
    HttpClient httpClient = new HttpClient();

    for (int i = 0; i < NUMBER_OF_REQUESTS; i  )
    {
        ProcessUrlAsync(httpClient);
    }
}

private async void ProcessUrlAsync(HttpClient httpClient)
{
    HttpResponseMessage httpResponse = null;

    try
    {
        Task<HttpResponseMessage> getTask = httpClient.GetAsync(URL);
        httpResponse = await getTask;

        Interlocked.Increment(ref _successfulCalls);
    }
    catch (Exception ex)
    {
        Interlocked.Increment(ref _failedCalls);
    }
    finally
    { 
        if(httpResponse != null) httpResponse.Dispose();
    }

    lock (_syncLock)
    {
        _itemsLeft--;
        if (_itemsLeft == 0)
        {
            _utcEndTime = DateTime.UtcNow;
            this.DisplayTestResults();
        }
    }
}

Çoklu uygulama en önemli kısmı aşağıda listelenmiştir:

public void TestParallel2()
{
    this.TestInit();
    ServicePointManager.DefaultConnectionLimit = 100;

    for (int i = 0; i < NUMBER_OF_REQUESTS; i  )
    {
        Task.Run(() =>
        {
            try
            {
                this.PerformWebRequestGet();
                Interlocked.Increment(ref _successfulCalls);
            }
            catch (Exception ex)
            {
                Interlocked.Increment(ref _failedCalls);
            }

            lock (_syncLock)
            {
                _itemsLeft--;
                if (_itemsLeft == 0)
                {
                    _utcEndTime = DateTime.UtcNow;
                    this.DisplayTestResults();
                }
            }
        });
    }
}

private void PerformWebRequestGet()
{ 
    HttpWebRequest request = null;
    HttpWebResponse response = null;

    try
    {
        request = (HttpWebRequest)WebRequest.Create(URL);
        request.Method = "GET";
        request.KeepAlive = true;
        response = (HttpWebResponse)request.GetResponse();
    }
    finally
    {
        if (response != null) response.Close();
    }
}

Çalışan testler çok iş parçacıklı versiyonu daha hızlı olduğunu ortaya çıkardı. 0.6 saniye civarında zaman uyumsuz bir yük aynı miktarda tamamlamak için yaklaşık 2 saniye sürdü iken 10k isteklerini tamamlamak için aldı. Bu zaman uyumsuz bir daha hızlı olmasını beklediğim için biraz sürpriz oldu. Belki HTTP telefonlarıma çok hızlı olduğu gerçeği nedeniyle oldu. Sunucu daha anlamlı bir işlemi gerçekleştirmek ve aynı zamanda bazı ağ gecikme olması gereken yerde gereken gerçek bir dünya senaryoda, sonuçları ters olabilir.

Bana endişeleri HttpClient ancak, ne yük artar davranır. Beri sürüyor etrafında 2 saniye teslim 10k mesajları sandim onu alacak yaklaşık 20 saniye sonra teslim 10 kat fazla sayıda mesaj, ama koşu testi gösterdi ihtiyacı yaklaşık 50 saniye ye teslim 100 bin mesaj. Ayrıca, genellikle 200 k mesajları iletmek için en fazla 2 dakika sürer ve genellikle, birkaç bin (3-4k) aşağıdaki durumla başarısız:

Bir yuva üzerindeki bir işlem sistemde yeterli arabellek alanı olmadığı için veya bir sıra dolu olduğundan yapılamadı.

IIS günlükleri kontrol ettim ve başarısız işlemleri hiçbir zaman sunucu var. İstemci içinde başarısız oldular. 65535 49152 olan kısa ömürlü bağlantı noktaları varsayılan aralığı ile bir Windows 7 makine üzerinde testler yaptım. Çalışan netstat 5-6k civarında port teoride çok daha fazla kullanılabilir olmalıydı, böylece test sırasında kullanılmakta olduğunu gösterdi. Eğer port olmaması gerçekten özel durumlar nedeni olsa da netstat düzgün durumumuz yoktu ya HttClient sonra, sadece özel durumlar yaratmak başlar noktaları maksimum sayıda kullanır.

Buna karşılık, HTTP aramalar üretme çoklu yaklaşım çok öngörülebilir davrandım. 1 milyon mesaj için 55 saniye civarında beklendiği gibi 10k mesajlar, iletiler için 100 bin civarında 5.5 saniye 0.6 saniye civarında aldım. Mesajların hiç biri başarısız oldu. O koşarken daha fazla, hiç RAM fazla 55 MB (Windows Görev yöneticisine göre) kullanılır. Bellek mesaj gönderirken kullanılan uyumsuz orantılı olarak yük ile büyüdü. 200 k mesajlar testler sırasında RAM 500 MB kullanmış.

Yukarıdaki sonuçlar için iki ana nedeni vardır sanırım. İlk HttpClient sunucu ile yeni bir bağlantı oluşturmak çok açgözlü gibi görünüyor. Kullanılan portları netstat tarafından bildirilen sayısının muhtemelen HTTP den fazla canlı tutma yararı yok demektir.

İkinci HttpClient azaltma mekanizması var gibi görünmüyor. Aslında bu genel bir sorun, zaman uyumsuz işlemler ile ilgili gibi görünüyor. Eğer işlemler çok sayıda gerçekleştirmek için gerekirse aynı anda başlamış olacak ve onların devamı için kullanılabilir olduğu gibi yürütülür. Teorik olarak bu yükü harici sistemlerde ama bu tamamen doğru değildir yukarıda kanıtladı Tamam, zaman uyumsuz işlemler nedeniyle olmalıdır. İstekleri büyük bir sayı aynı anda başlayan bellek kullanımını artırmak ve tüm yürütme yavaşlatacaktır.

Daha iyi sonuçlar, bellek ve yürütme zamanı akıllıca edinmek için basit ama ilkel bir gecikme mekanizması ile zaman uyumsuz istekleri sayısını sınırlayarak başardım:

public async void TestAsyncWithDelay()
{
    this.TestInit();
    HttpClient httpClient = new HttpClient();

    for (int i = 0; i < NUMBER_OF_REQUESTS; i  )
    {
        if (_activeRequestsCount >= MAX_CONCURENT_REQUESTS)
            await Task.Delay(DELAY_TIME);

        ProcessUrlAsyncWithReqCount(httpClient);
    }
}

Eğer HttpClient eşzamanlı istek sayısını sınırlamak için bir mekanizma dahil olursa gerçekten yararlı olacaktır. Görev sınıfı kullanırken .dayanan (Net iş parçacığı havuzu) azaltma Otomatik olarak eşzamanlı iş parçacığı sayısını sınırlayarak elde edilir.

Tam bir bakış için de zaman uyumsuz test HttpWebRequest yerine HttpClient dayalı bir versiyonunu oluşturdum ve çok daha iyi sonuçlar elde etmeyi başardı. Bir başlangıç için, eş zamanlı bağlantı sayısı (ServicePointManager.bir sınır ayarı sağlar DefaultConnectionLimit veya üzerinden config) anlamına gelir hiç kalmamış limanlar ve asla başarısız ricaları (HttpClient, varsayılan olarak, dayandığı HttpWebRequest, ama göründüğü için göz ardı bağlantı sınırı ayarı).

Zaman uyumsuz HttpWebRequest yaklaşım hala 50 - ` çoklu bir daha yavaş üzereydi, ama öngörülebilir ve güvenilir oldu. Bunun tek dezavantajı büyük yük altında bellek büyük miktarda kullanılmış olmasıydı. Örneğin 1 milyon istekleri göndermek için yaklaşık 1.6 GB ihtiyacı vardı. Eşzamanlı istek sayısı yukarıda HttpClient için benim yaptığım gibi) sınırlayarak sadece 20 MB kullanılan bellek azaltmak ve yürütme bir zaman sadece  daha yavaş çoklu yaklaşım daha elde etmeyi başardım.

Bu uzun sunumun ardından, benim sorular şunlardır: HttpClient sınıfı .4.5 yoğun yük uygulamaları için kötü bir seçim Net? Hakkında bahsettiğim sorunları çözmek gerekir hangi gaz için herhangi bir yolu var mı? Hakkında uyumsuz lezzet HttpWebRequest?

Güncelleme (@Stephen Cleary teşekkürler)

Görünüşe göre, HttpClient, sadece HttpWebRequest hangi varsayılan olarak kuruludur () gibi, aynı ana ServicePointManager ile sınırlı üzerinde eş zamanlı bağlantı sayısı var.DefaultConnectionLimit. Garip bir şey MSDN bağlantı için varsayılan değer göre sınırı 2. Ben de benim tarafımda gerçekten 2 sivri olan hata ayıklayıcısını kullanarak varsayılan değer olup olmadığını kontrol etti. Ancak, bu sürece açıkça ServicePointManager değeri ayarlama gibi görünüyor.DefaultConnectionLimit, varsayılan değeri yok sayılır. Açıkça HttpClient testlerim sırasında değerini ayarlayın alamadığım göz ardı olduğunu düşündüm.

ServicePointManager ayarladıktan sonra.DefaultConnectionLimit 100 HttpClient güvenilir ve öngörülebilir sadece 100 bağlantı noktalarının kullanıldığını doğruluyor netstat () oldu. Hala uyumsuz HttpWebRequest (40%) daha yavaş, ama garip bir şekilde, daha az bellek kullanıyor. 1 milyon isteklerini içeren test için 550 MB en fazla, zaman uyumsuz HttpWebRequest 1.6 GB göre kullanılır.

Bu yüzden, birlikte HttpClient süre ServicePointManager.DefaultConnectionLimit görünüyor sağlamak için güvenilirlik (en azından senaryosu, tüm aramalar yapılmış olma yolunda aynı host), hala görünüşe göre onun performansı olumsuz etkilenen tarafından eksikliği uygun bir azaltma mekanizması. Yapılandırılabilir bir değere istekleri eşzamanlı sayısını sınırlamak ve bir sıra içinde kalan yüksek çok daha uygun olurdu koyacak bir şey ölçülebilirlik senaryolar.

CEVAP
13 Mayıs 2013, PAZARTESİ


Ayrıca bu sınavlarda soru, geçenlerde oluşturulan bazı yenilikler içeren çok az HTTP çağrıları (5000 göre 1 milyon daha önce) ama istekleri çok daha uzun sürdü yürütmek (500 milisaniye göre yaklaşık 1 milisaniye daha önce). Her iki test uygulamaları, eşzamanlı olarak birden çok iş parçacığı (temel HttpWebRequest) ve zaman uyumsuz G/Ç (tabanlı HTTP istemci) üretilen benzer bulgular: yaklaşık 10 saniye çalıştırmak için kullanarak yaklaşık %3 CPU ve 30 MB hafıza. İki test arasındaki tek fark, birden çok iş parçacığı, bir zaman uyumsuz biri sadece 22 iken 310 iş parçacığı çalıştırmak için kullanılır. Yani bir uygulama olur birlikte her iki g/Ç bağımlı ve CPU bağlı işlem zaman uyumsuz sürümü olurdu üretilen daha iyi olur çünkü orada olurdu, daha fazla CPU süresi için kullanılabilir iş parçacığı performans CPU işlemleri, olanlar gerçekten ihtiyaç (iş parçacığı bekleyen G/Ç işlemleri tamamlandı sadece israf).

Benim testler için sonuç olarak, asenkron HTTP aramalar çok hızlı istekleri ile uğraşırken en iyi seçenek değildir. Neden arkasında o zaman çalışan bir görev içeren bir zaman uyumsuz G/Ç Ara, iplik üzerinde hangi görev olduğunu başlatıldı çıkın en kısa sürede olarak zaman uyumsuz çağrı yapılır ve geri kalan görevidir kayıtlı olarak bir geri arama. G/Ç işlemi tamamlandığında, daha sonra geri ilk kullanılabilir iş parçacığı yürütme için sıraya. Tüm bu işlemler O bunları başlatan iş parçacığı çalıştırıldığında daha etkili olmak/hızlı kılan bir yük oluşturur.

HTTP aramalar asenkron uzun ile uğraşırken ya da potansiyel olarak uzun herhangi bir iş parçacığı yoğun G/Ç işlemi tamamlamak için bekleyen tutmak yok çünkü G/Ç işlemleri iyi bir seçenek. Bu konuları bir uygulama daha fazla CPU zamanı CPU bağlı işlemleri tarafından harcanması için izin tarafından kullanılan toplam sayısı azalır. Ayrıca, uygulamaları tek ayırmak sınırlı sayıda konu (gibi dava ile web uygulamaları), zaman uyumsuz G/Ç iş parçacığı havuzu iş parçacığı engeller tükenmesi, hangi olabilir eğer performans g/Ç çağrıları eş zamanlı olarak.

Bu yüzden, zaman uyumsuz HttpClient yoğun yük uygulamaları için bir darboğaz değil. Sadece bu onun yapısı değil çok uygun, çok hızlı HTTP istekleri yerine bu ideal için uzun ya da potansiyel olarak uzun olanları, özellikle de içindeki uygulamalar sadece sınırlı sayıda iş parçacığı kullanılabilir. Ayrıca, ServicePointManager üzerinden eşzamanlılık sınırlamak için iyi bir uygulamadır.Paralellik iyi bir düzeyde sağlamak için yeterince yüksek, ama kısa ömürlü bağlantı noktası tükenme önlemek için yeterince düşük bir değer ile DefaultConnectionLimit. Testler ve sonuçlar bu soru here için sunulan hakkında daha fazla bilgi bulabilirsiniz.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Chip Johnson

    Chip Johnson

    30 AĞUSTOS 2007
  • fireflame65

    fireflame65

    27 Mart 2007
  • Mr. H

    Mr. H

    1 Temmuz 2012