2012-12-27 13 views
46

Yapmaya çalıştığım şey şu: On binlerce mobil cihazdan (redis ile işbirliği) amaç için uzun bağlantıları tutacak bir Node.js http sunucusu geliştiriyorum tek bir makinede müşteriler.Node.js ile uzun bağlantılar, bellek kullanımını azaltma ve bellek sızıntısını nasıl önleme? Ayrıca, V8 ve webkit-devtools ile ilgili:

Test ortamı: İlk anda

1.80GHz*2 CPU/2GB RAM/Unbuntu12.04/Node.js 0.8.16 

, ben modül "ekspres" hangi ile ben RAM yeterli değildir yani kullanılan takas önce yaklaşık 120k eşzamanlı bağlantı ulaşabilir kullandı. Daha sonra, yerel "http" modülüne geçtim, yaklaşık 160k'a kadar eşzamanlılık elde ettim. Ancak, yerel http modülünde gerek duymadığım çok fazla işlevsellik olduğunu fark ettim, bu yüzden yerel "net" modülüne geçtim (bu, http protokolünü kendi başıma kullanmam gerektiği anlamına geliyor, ama bu sorun değil). Şimdi, tek bir makine için yaklaşık 250 bin eşzamanlı bağlantıya ulaşabilirim. İşte

benim kodlarının ana yapıdır: Son olarak

var net = require('net'); 
var redis = require('redis'); 

var pendingClients = {}; 

var redisClient = redis.createClient(26379, 'localhost'); 
redisClient.on('message', function (channel, message) { 
    var client = pendingClients[channel]; 
    if (client) { 
     client.res.write(message); 
    } 
}); 

var server = net.createServer(function (socket) { 
    var buffer = ''; 
    socket.setEncoding('utf-8'); 
    socket.on('data', onData); 

    function onData(chunk) { 
     buffer += chunk; 
     // Parse request data. 
     // ... 

     if ('I have got all I need') { 
      socket.removeListener('data', onData); 

      var req = { 
       clientId: 'whatever' 
      }; 
      var res = new ServerResponse(socket); 
      server.emit('request', req, res); 
     } 
    } 
}); 

server.on('request', function (req, res) { 
    if (res.socket.destroyed) {    
     return; 
    } 

    pendingClinets[req.clientId] = { 
     res: res 
    }; 

    redisClient.subscribe(req.clientId); 

    res.socket.on('error', function (err) { 
     console.log(err); 
    }); 

    res.socket.on('close', function() { 
     delete pendingClients[req.clientId]; 

     redisClient.unsubscribe(req.clientId); 
    }); 
}); 

server.listen(3000); 

function ServerResponse(socket) { 
    this.socket = socket; 
} 
ServerResponse.prototype.write = function(data) { 
    this.socket.write(data); 
} 

, benim sorular şunlardır: o uzak eşzamanlılık artırmak böylece

  1. nasıl bellek kullanımını azaltabilir?

  2. Ben node.js işlem bellek kullanımını nasıl hesaplanacağı konusunda gerçekten karıştı. Chrome V8 motordan node.js biliyorum orada process.memoryUsage() API olduğunu ve üç değerleri döndürür: rss/heapTotal/heapUsed, ben daha ilgili olması parçası Aralarındaki fark, bu, ve tam olarak ne bileþimini Node.js işlem tarafından kullanılan bellek?

  3. Ben bazı testler yaptık ve bir sorun var görünmüyor olsa bile bellek sızıntısı endişeleniyordu. Endişe etmem gereken ya da herhangi bir tavsiyem var mı?
  4. Ben açıklandığı gibi, sadece yukarıda benim kodları gibi benim genel nesneyipendingClients clientid tarafından adında bir özellik eklediğinizde o, demek yeni bir gizli sınıf olmak olacak, V8 hidden class hakkında bir dokümana bulundu oluşturulan? Doz bellek sızıntısına neden olur?
  5. Ben node.js sürecinin yığın haritasını analiz etmek webkit-devtools-agent kullandı. İşlemi başlattım ve bir yığın fotoğrafını çektim, daha sonra 10k istekleri yolladım ve daha sonra bunları kestim, bundan sonra tekrar bir yığın fotoğraf çektim. Bu iki anlık görüntü arasındaki farkı görmek için karşılaştırması bakış açısını kullandım. İşte sahip olduğum şey: enter image description here Bunu açıklayan var mı? (Dizi)/(derlenmiş kod)/(dizgi)/Komut/Dizi sayısı ve boyutu çok arttı, bu ne anlama geliyor?

DÜZENLEME: nasıl yükleme testi kaçtın?
1. İlk olarak,
1.1 (bir makine en fazla 16 bit ile temsil edilen 60k + bağlantı() olması nedeniyle, birden fazla 60k eşzamanlılık birden fazla istemci makine mi elde etmek için) bir sunucu makinesinde parametreleri hem de müşteri makineleri modifiye .Sunucu ve istemci makineleri, ben dosya tanıtıcı test programı çalıştırmak olacak kabuğunda bu komutlarını kullanmak modifiye Hem tek:

ulimit -Hn 999999 
ulimit -Sn 999999 

1,2. Sunucu makinede, biraz da net/tcp ilgili çekirdek parametrelerini modifiye, en önemli olanları şunlardır:

net.ipv4.tcp_mem = 786432 1048576 26777216 
net.ipv4.tcp_rmem = 4096 16384 33554432 
net.ipv4.tcp_wmem = 4096 16384 33554432 

1,3. istemci makinelere gibi:

net.ipv4.ip_local_port_range = 1024 65535 

2. İkinci olarak, özel bir yazdı çoğu yük testi araçları beri, vb ab, kuşatma, kısa bağlantıları için, ama ben değilim, node.js kullanarak istemci programı simüle uzun bağlantıları kullanarak ve bazı özel gereksinimleri var. 3.
Sonra tek bir makinede sunucu programı ve diğer üç ayrı makinelere üç müşteri programı başladı.

DÜZENLEME: bunu çok anlamlı ve pratik değil, dışarı tek bir makinede (2GB RAM) üzerinde 250k eşzamanlı bağlantı ulaşmak yaptı ama döndü. Çünkü bir bağlantı kurulduğunda, bağlantıyı beklemede bırakıyorum, başka bir şey yok. Onlara yanıt göndermeye çalıştığımda, eşzamanlılık sayısı yaklaşık 150 bin dolar civarına düştü. Ben hesaplanırken, bağlantı başına yaklaşık 4KB daha fazla bellek kullanımı var, bu net.ipv4.tcp_wmem ile ilgili olduğunu düşünüyorum olarak ayarlanmış, ama ben daha küçük, hiçbir şey değişti. Nedenini anlayamıyorum.

DÜZENLEME: Aslında, şimdi ben tcp bağlantı başına ne kadar bellek kullanır ilgileniyorum ve tek bağlantısı tarafından kullanılan belleğin tam olarak kompozisyonu nedir? benim test verilerine göre: 1800M RAM hakkında tüketilen

150k eşzamanlılık ve node.js süreci (ücretsiz -m çıkış itibaren)

Sonra ben bu farz yaklaşık 600M RSS vardı:

  • (1800M - 600M)/150K = 8k, bu iki parçadan oluşur, bir bağlantı çekirdek TCP küme bellek kullanımı: tampon (4 KB) + yazma tamponu (okuma 4KB) (Aslında bu, sistemin nasıl bu tamponlar için kullanmak ne kadar bellek belirler?)

  • net.ipv4.tcp_rmem ve net.ipv4.tcp_wmem yukarıdaki benim ayarını eşleşmiyor 600M/150k = 4k, bu tek bir bağlantı

ait node.js bellek kullanımı Birazdan muyum nedir? Bellek kullanımını her iki açıdan nasıl azaltabilirim?

Eğer iyi tanımlamamış bir yer varsa, bana bildirin, ben onu daraltacağım! Herhangi bir açıklama veya tavsiye takdir edilecektir, teşekkürler!

+2

İlk izlenim, bu özelliklerin bulunduğu bir makinede 250k'nın şaşırtıcı olmasıdır. Şu an için endişelendiğin tüm kullanıcılara odaklanmak için zaman olabilir. = P – tehgeekmeister

+0

Eşzamanlı bağlantıların sayısını nasıl ölçüyorsunuz? – tehgeekmeister

+0

Yan not: Bir stackexchange sitesinden istendiğinde daha az sayıda belirli soruna bağlı kalmak en iyisidir. Bu şekilde daha fazla cevap alacaksınız. – tehgeekmeister

cevap

5
  1. Bellek kullanımını azaltma konusunda endişelenmenize gerek yok.Dahil ettiğiniz bu okumadan, akla yatkın olan minimum minimum seviyeye oldukça yakın görünüyorsunuz (bir birim belirtilmemişse standart olan bayt olarak yorumladım).

  2. Bu, yanıtlayabildiğimden daha ayrıntılı bir sorudur, ancak işte RSS. Öbek, en iyi anladığım kadarıyla, dinamik olarak ayrılmış belleğin unix sistemlerinden geldiği yerdir. Öyleyse, yığın toplamı, kullanımınız için yığına ayrılan her şeymiş gibi görünürken, kullanılan yığın, kullandığınız miktarın ne kadarının kullanıldığıdır.

  3. Bellek kullanımınız oldukça iyi ve aslında bir sızıntınız yok gibi görünüyor. Henüz endişelenmem. ==

  4. Bilmiyor.

  5. Bu anlık görüntü makul görünüyor. Taleplerin artmasıyla yaratılan bazı nesnelerin çöp toplandığını ve başkalarının bunu yapmadığını düşünüyorum. 10k nesneleri üzerinde hiçbir şey olmadığını görüyorsunuz ve bu nesnelerin çoğu oldukça küçük. Ben buna iyi derim.

Daha da önemlisi, bunun nasıl test edildiğini merak ediyorum. Daha önce olduğu gibi büyük yük testi yapmayı denedim ve çoğu araç, açık dosya tanımlayıcılarının sayısına (genellikle varsayılan olarak işlem başına bin başına yaklaşık olarak) bağlı olarak linux üzerinde bu tür bir yük oluşturmayı başaramaz.). Ayrıca, bir priz kullanıldığında, tekrar kullanım için hemen kullanılabilir değildir. Tekrar hatırlayacağım gibi, bir dakikanın önemli bir kısmını alır. Bunun yanı sıra normalde 100k'ın altında bir yerde bulunan sistem açık dosya tanımlayıcı sınırını gördüğüm gerçeğim, değiştirilmemiş bir kutuda o kadar fazla yük almanın ya da tek bir kutuda üretmenin mümkün olmadığından emin değilim. Böyle bir adımdan bahsetmediğinizden, sanırım ne yaptığınızı yaptığından emin olmak için yük testlerinizi de araştırmanız gerekebilir.

+0

Sınamayı nasıl yürüttüğümle ilgili güncellemeyi güncelledim. “Soket kullanıldıktan sonra, tekrar kullanmak için hemen kullanılamaz” demiştiniz, senaryoda böyle bir sorun yok, uzun bağlantılar kullanıyorum. Ve sanırım müşteri tarafında geçici prizden bahsediyorsun, "soket" değil. –

+0

Err, Dosya tanıtıcıyı kastettim. Bence. Olabilir. Gerçekten, bu şeyleri ne kadar iyi anladığımı sınırlara ulaşıyoruz. – tehgeekmeister

+0

Bu sınırlamayı ** ulimit ** komutuyla kaldırdım. Ve yardımın için teşekkürler, bu arada, ben bir ingilizce konuşmacı değilim, belki de iyi açıklamamış bir şey var, bilmeme izin ver, elimden gelenin en iyisini yapacağım. :) –

2

Sadece birkaç notlar:

bir nesne res sarmak gerekir mi {res: res} Az önce doğrudan

pendingClinets[req.clientId] = res; 

DÜZENLEME yardımcı olabilecek başka ~ mikro optimizasyon atayabilirsiniz

server.emit('request', req, res); 

iki argümanı 'request' olarak geçirir, ancak istek işleyiciniz gerçekten yalnızca 'res' yanıtına gereksinim duyar.

res['clientId'] = 'whatever'; 
server.emit('request', res); 

fiili verilerin sizin miktarı size referans işaretçisi (birkaç bayt) kurtaracak 'isteği' işleyicileri argümanlar listesinde 1 daha az tartıştığını, aynı kalırken. Ancak yüzbinlerce bağlantıyı işlerken birkaç bayt ekleyebilirsiniz. Ayrıca, dış çağrıdaki ek argümanı işlemek için küçük işlemci yükünü de kaydedeceksiniz.

+0

Evet, doğrudan için çalışacağını ata, ben de deneyeceğim. 'Hata' olayı ile ilgili olarak, Node.js resmi belgesi şunları söylüyor: ** 'Kapat' etkinliği bu olayı doğrudan takip edecek **, bu yüzden yaklaşımımın iyi olduğunu düşünüyorum. Bu notlar için teşekkürler. –

+0

@Aaron Wang - Ben RTFM'd gerekir, ben onu düzenledim ve sunucunuzdan biraz daha fazla gıcırdamanıza yardımcı olabilecek başka bir küçük optimizasyon ekledim. –

+0

** res ** 'a ** pendingClients ** nesnesine atama yapma, bazı bellekleri kaydetme, her 60k bağlantıda yaklaşık 20M, teşekkürler! Yeni notunuza gelince, neden bu şekilde resmi bir http modülü ile aynı arabirimi sağladığımı ve aslında kayıt sırasında istek bilgilerini takip etmek için ** req ** kullandım ama bu bilgileri yukarıdaki kodlarda göstermedim. basitleştirme için. Diğer sebep ise ** req ** 'nin çöp toplanmasıdır, bu yüzden bu konuda çok endişelenmiyorum. –

İlgili konular