2012-12-26 17 views
9

के साथ प्रति सेकेंड प्राप्त करता है SocketAsyncEventArgs का उपयोग करके एक टीसीपी सर्वर विकसित किया जाता है और यह विंडोज सेवा के रूप में async विधियां है। मेरे पास मुख्य की शुरुआत में कोड की ये 2 पंक्ति है:20 सॉकेटएसिंसेन्ट आर्ट्स

ThreadPool.SetMaxThreads(15000, 30000); 
ThreadPool.SetMinThreads(10000, 20000); 

और दोनों सत्य वापस लौटे (लौटे गए मान लॉग हैं)। अब 2000 से 3000 क्लाइंट इस सर्वर पर संदेश भेजना शुरू कर देते हैं और यह कनेक्शन स्वीकार करना शुरू कर देता है (मैं कनेक्शन की संख्या गिनता हूं और यह अपेक्षित है - एक कनेक्शन पूल है)। सर्वर प्रक्रिया की थ्रेड गिनती ~ 2050 से ~ 3050 तक बढ़ेगी। अब तक सब ठीक है!

अब एक प्राप्त विधि है जिसे ReceiveAsync सत्य या SocketAsyncEventArgs की पूर्ण घटना के बाद या तो कॉल किया जाएगा।

और यहां समस्याएं शुरू होती हैं: इससे कोई फर्क नहीं पड़ता कि ग्राहक कितने संदेश जुड़े हैं और कितने संदेश भेजते हैं, प्राप्त किया जाएगा एक सेकंड में अधिकतम 20 बार कहा जाएगा! और जैसे-जैसे ग्राहकों की संख्या बढ़ जाती है, यह संख्या (20) ~ 10 तक गिर जाती है।

पर्यावरण: टीसीपी सर्वर और क्लाइंट एक ही मशीन पर अनुकरण किए जा रहे हैं। मैंने 2 मशीनों पर कोड का परीक्षण किया है, एक में 2-कोर सीपीयू और 4 जीबी रैम है और दूसरे में 8-कोर सीपीयू और 12 जीबी रैम है। कोई डेटा हानि नहीं है (अभी तक) और कभी-कभी मुझे प्रत्येक प्राप्त ऑपरेशन में 1 से अधिक संदेश प्राप्त होते हैं। कोई बात नहीं। लेकिन प्राप्त करने की संख्या कैसे बढ़ाया जा सकता है?

कार्यान्वयन पर अतिरिक्त नोट्स: कोड बड़ा है और इसमें कई अलग-अलग तर्क शामिल हैं। एक समग्र विवरण होगा: मेरे पास नए कनेक्शन स्वीकार करने के लिए एक एकल सॉकेटएसिंसेन्ट आर्ट्स है। यह बहुत अच्छा काम करता है। अब प्रत्येक नए स्वीकृत कनेक्शन के लिए मैं डेटा प्राप्त करने के लिए एक नया सॉकेटएसिंसेन्ट आर्ट्स बना देता हूं। मैंने पूल में यह एक (सॉकेटएसिंसेन्ट आर्ट्स प्राप्त करने के लिए बनाया) रखा है। इसका पुन: उपयोग नहीं किया जाएगा लेकिन कनेक्शन को ट्रैक करने के लिए उपयोगकर्ता टोकन का उपयोग किया जा रहा है; उदाहरण के लिए उन कनेक्शन जो डिस्कनेक्ट हैं या वे कनेक्शन जो 7 मिनट के लिए कोई डेटा नहीं भेजते हैं उन्हें बंद कर दिया जाएगा और डिस्प्ले किया जाएगा (सॉकेटएसिंसेन्ट एआरजीएस की स्वीकार्य सॉकेट बंद हो जाएगी (दोनों), बंद और डिस्पोजेड और इसी तरह सॉकेटएसिंक एवेन्ट ऑब्जेक्ट ऑब्जेक्ट होगा)।

class Sudo 
{ 
    Socket _listener; 
    int _port = 8797; 

    public Sudo() 
    { 
     var ipEndPoint = new IPEndPoint(IPAddress.Any, _port); 
     _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     _listener.Bind(ipEndPoint); 

     _listener.Listen(100); 

     Accept(null); 
    } 

    void Accept(SocketAsyncEventArgs acceptEventArg) 
    { 
     if (acceptEventArg == null) 
     { 
      acceptEventArg = new SocketAsyncEventArgs(); 
      acceptEventArg.Completed += AcceptCompleted; 
     } 
     else acceptEventArg.AcceptSocket = null; 

     bool willRaiseEvent = _listener.AcceptAsync(acceptEventArg); ; 

     if (!willRaiseEvent) Accepted(acceptEventArg); 
    } 

    void AcceptCompleted(object sender, SocketAsyncEventArgs e) 
    { 
     Accepted(e); 
    } 

    void Accepted(SocketAsyncEventArgs e) 
    { 
     var acceptSocket = e.AcceptSocket; 
     var readEventArgs = CreateArg(acceptSocket); 

     var willRaiseEvent = acceptSocket.ReceiveAsync(readEventArgs); 

     Accept(e); 

     if (!willRaiseEvent) Received(readEventArgs); 
    } 

    SocketAsyncEventArgs CreateArg(Socket acceptSocket) 
    { 
     var arg = new SocketAsyncEventArgs(); 
     arg.Completed += IOCompleted; 

     var buffer = new byte[64 * 1024]; 
     arg.SetBuffer(buffer, 0, buffer.Length); 

     arg.AcceptSocket = acceptSocket; 

     arg.SocketFlags = SocketFlags.None; 

     return arg; 
    } 

    void IOCompleted(object sender, SocketAsyncEventArgs e) 
    { 
     switch (e.LastOperation) 
     { 
      case SocketAsyncOperation.Receive: 
       Received(e); 
       break; 
      default: break; 
     } 
    } 

    void Received(SocketAsyncEventArgs e) 
    { 
     if (e.SocketError != SocketError.Success || e.BytesTransferred == 0 || e.Buffer == null || e.Buffer.Length == 0) 
     { 
      // Kill(e); 
      return; 
     } 

     var bytesList = new List<byte>(); 
     for (var i = 0; i < e.BytesTransferred; i++) bytesList.Add(e.Buffer[i]); 

     var bytes = bytesList.ToArray(); 

     Process(bytes); 

     ReceiveRest(e); 

     Perf.IncOp(); 
    } 

    void ReceiveRest(SocketAsyncEventArgs e) 
    { 
     e.SocketFlags = SocketFlags.None; 
     for (int i = 0; i < e.Buffer.Length; i++) e.Buffer[i] = 0; 
     e.SetBuffer(0, e.Buffer.Length); 

     var willRaiseEvent = e.AcceptSocket.ReceiveAsync(e); 
     if (!willRaiseEvent) Received(e); 
    } 

    void Process(byte[] bytes) { } 
} 
+0

एमबी रैम? क्या मैं कैश हूं? मुझे उम्मीद है कि आपके पास कई जीबी मेमोरी है। क्या आपने बाधाओं को देखने के लिए संसाधन मॉनिटर या प्रोसेस एक्सप्लोरर का उपयोग किया है? – HABO

+0

हम्म ... आपके सर्वर पर एसिंक्रोनस आर्किटेक्चर क्या है? क्या आप कुछ छद्म कोड पोस्ट कर सकते हैं ताकि यह वर्णन किया जा सके कि सुनो कैसे होता है और कनेक्शन कैसे स्वीकार किए जाते हैं आदि? –

+0

@HABO धन्यवाद! मैंने इसे सही किया है। –

उत्तर

4

: यहाँ एक Sudo वर्ग है कि इन कार्य लेकिन अन्य सभी तर्क और लॉगिंग और त्रुटि जाँच और कुछ और करता है यह सरल और स्पष्ट बनाने के लिए निकाल दिया जाता है (हो सकता है तो यह समस्या पैदा करने वाले कोड को पहचानना आसान है) है कारण यह धीमा हो रहा है क्योंकि उन थ्रेडों में से प्रत्येक को संदर्भ स्विच की आवश्यकता होती है, और यह अपेक्षाकृत महंगा संचालन है। आपके द्वारा जोड़े गए अधिक थ्रेड, आपके सीपीयू का बड़ा प्रतिशत केवल संदर्भ स्विचिंग पर खर्च किया जाता है, न कि आपके वास्तविक कोड में।

आपने इस एक-थ्रेड-प्रति-क्लाइंट की बाधा को एक अजीब तरीके से मारा है। सर्वर-साइड एसिंक का पूरा बिंदु को थ्रेड की संख्या को कम करने के लिए है - प्रति क्लाइंट एक थ्रेड नहीं है, लेकिन आदर्श रूप से आपके सिस्टम में प्रत्येक लॉजिकल प्रोसेसर के लिए केवल एक या दो है।

आपके द्वारा पोस्ट किया गया एसिंक कोड ठीक दिखता है, इसलिए मैं केवल अनुमान लगा सकता हूं कि आपकी Process विधि में गैर-एसिंक को अनदेखा करना आसान है, इसमें I/O अवरुद्ध करना, यानी डेटाबेस या फ़ाइल एक्सेस। जब I/O ब्लॉक होता है, तो .NET थ्रेड पूल इसका पता लगाता है और स्वचालित रूप से एक नया धागा फैलाता है - यह मूल रूप से Process में I/O के साथ एक बाधा के रूप में नियंत्रण से बाहर हो रहा है।

एक एसिंक पाइपलाइन को वास्तव में 100% एसिंक होने की आवश्यकता है ताकि इससे कोई महत्वपूर्ण लाभ प्राप्त हो सके। हाफ-इन में आप जटिल कोड लिखेंगे जो सरल सिंक कोड के रूप में उतना ही खराब प्रदर्शन करता है।

यदि आप पूरी तरह से Process विधि पूरी तरह से async नहीं बना सकते हैं, तो आप इसे कुछ भाग्य बना सकते हैं।चीजें एक छोटे, सीमित आकार के धागे पूल द्वारा प्रसंस्करण के लिए कतार में प्रतीक्षा करें।

+0

इस विंडोज़ सेवा में मैं स्मृति में प्राप्त डेटा पर कुछ प्रोसेसिंग करता हूं और एकमात्र जगह जो मैं अपने प्रोग्राम से बाहर बोल रहा हूं वह एक तरीका है जो प्रोसेस किए गए डेटा को एमएसएमक्यू कतार में रखता है। –

+0

आपने डेटाबेस और फ़ाइलों (और मेरे मामले में एमएसएमक्यू) के बारे में क्या बताया है। तुम सही थे। मैं अपनी अन्य परियोजनाओं में वसूली योग्य विकल्प के साथ लानत एमएसएमक्यू के बारे में भूल गया (उदाहरण के लिए खरगोश एमक्यू की तुलना में इसका एक भयानक प्रदर्शन है, लेकिन यहां मुझे इसका उपयोग करने के लिए मजबूर किया गया है - एमएसएमक्यू प्रदर्शन पर किसी भी टिप्पणी का भी स्वागत है)। इसलिए कृपया मुख्य एसिंक वर्कफ़्लो से अन्य गतिविधियों के कारण होने वाली समस्या को ठीक से इंगित करने के लिए अपना उत्तर संपादित करें; इसलिए मैं इसे उत्तर के रूप में चिह्नित कर सकता हूं। एक बार फिर धन्यवाद! –

+0

बहुत बढ़िया, खुशी है कि आपको एक जवाब मिला। हाँ, एमएसएमक्यू वास्तव में चूसना करता है जब पुनर्प्राप्ति योग्य मोड चालू होता है :)। –

संबंधित मुद्दे