AsNet HttpClient .Net 4.5 से गहन लोड अनुप्रयोगों के लिए एक बुरा विकल्प है?


130

मैंने हाल ही में HTTP कॉल थ्रूपुट का परीक्षण करने के लिए एक सरल एप्लिकेशन बनाया है जो कि एक असंगत तरीके बनाम शास्त्रीय बहुपरत दृष्टिकोण में उत्पन्न किया जा सकता है।

एप्लिकेशन HTTP कॉल की एक पूर्वनिर्धारित संख्या करने में सक्षम है और अंत में यह उन्हें प्रदर्शन करने के लिए आवश्यक कुल समय प्रदर्शित करता है। मेरे परीक्षणों के दौरान, सभी HTTP कॉल मेरे स्थानीय IIS को गंभीर बना दिया गया था और उन्होंने एक छोटी पाठ फ़ाइल (आकार में 12 बाइट्स) को पुनर्प्राप्त किया था।

अतुल्यकालिक कार्यान्वयन के लिए कोड का सबसे महत्वपूर्ण हिस्सा नीचे सूचीबद्ध है:

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();
        }
    }
}

मल्टीथ्रेडिंग कार्यान्वयन का सबसे महत्वपूर्ण हिस्सा नीचे सूचीबद्ध है:

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();
    }
}

परीक्षणों को चलाने से पता चला कि मल्टीथ्रेडेड संस्करण तेज था। 10k अनुरोधों को पूरा करने के लिए इसे लगभग 0.6 सेकंड का समय लगा, जबकि async एक को लोड की समान मात्रा के लिए लगभग 2 सेकंड का समय लगा। यह थोड़ा आश्चर्यचकित करने वाला था, क्योंकि मुझे उम्मीद थी कि एसिंक्स एक तेज होगा। शायद यह इस तथ्य के कारण था कि मेरी HTTP कॉल बहुत तेज थीं। एक वास्तविक विश्व परिदृश्य में, जहां सर्वर को अधिक सार्थक संचालन करना चाहिए और जहां कुछ नेटवर्क विलंबता भी होनी चाहिए, परिणाम उलट हो सकते हैं।

हालांकि, लोड बढ़ने पर HttpClient के व्यवहार के तरीके से मुझे वास्तव में चिंता होती है। चूँकि 10k संदेशों को देने में इसे लगभग 2 सेकंड का समय लगता है, मैंने सोचा कि संदेशों की संख्या को 10 गुना करने के लिए इसे 20 सेकंड के आसपास ले जाएगा, लेकिन परीक्षण चलाने से पता चला कि 100k संदेशों को वितरित करने के लिए लगभग 50 सेकंड की आवश्यकता है। इसके अलावा, आमतौर पर 200k संदेश देने में 2 मिनट से अधिक समय लगता है और अक्सर, उनमें से कुछ हजारों (3-4k) निम्नलिखित अपवाद के साथ विफल हो जाते हैं:

सॉकेट पर एक ऑपरेशन नहीं किया जा सका क्योंकि सिस्टम में पर्याप्त बफर स्थान की कमी थी या क्योंकि एक कतार भरी हुई थी।

मैंने IIS लॉग और ऑपरेशंस को चेक किया जो विफल हो गया जो कभी भी सर्वर को नहीं मिला। वे ग्राहक के भीतर विफल रहे। मैंने एक विंडोज 7 मशीन पर 49152 से 65535 के अल्पकालिक बंदरगाहों की डिफ़ॉल्ट सीमा के साथ परीक्षणों को चलाया। नेटस्टैट चलाने से पता चला कि परीक्षण के दौरान लगभग 5-6k बंदरगाहों का उपयोग किया जा रहा था, इसलिए सिद्धांत रूप में कई और उपलब्ध होने चाहिए थे। यदि बंदरगाहों की कमी वास्तव में अपवादों का कारण थी, तो इसका मतलब है कि या तो नेटस्टैट ने स्थिति को ठीक से रिपोर्ट नहीं किया या HttClient केवल अधिकतम संख्या में पोर्ट का उपयोग करता है जिसके बाद वह अपवादों को फेंकना शुरू कर देता है।

इसके विपरीत, HTTP कॉल जनरेट करने के बहुपरत दृष्टिकोण ने बहुत पूर्वानुमानित व्यवहार किया। मैंने इसे 10k संदेशों के लिए 0.6 सेकंड के आसपास, 100k संदेशों के लिए 5.5 सेकंड के आसपास और 1 मिलियन संदेशों के लिए 55 सेकंड के आसपास अपेक्षित के रूप में लिया। कोई भी संदेश विफल नहीं हुआ। इसके अलावा, जब यह चलता था, तो यह 55 एमबी से अधिक रैम (विंडोज टास्क मैनेजर के अनुसार) का उपयोग नहीं करता था। मैसेज भेजने के दौरान उपयोग की जाने वाली मेमोरी, एसिंक्रोनसली लोड के साथ आनुपातिक रूप से बढ़ती है। यह 200k संदेश परीक्षणों के दौरान लगभग 500 एमबी रैम का उपयोग करता था।

मुझे लगता है कि उपरोक्त परिणामों के दो मुख्य कारण हैं। पहला यह है कि HttpClient सर्वर के साथ नए कनेक्शन बनाने में बहुत लालची लगता है। Netstat द्वारा रिपोर्ट किए गए उपयोग किए गए बंदरगाहों की उच्च संख्या का मतलब है कि यह संभवतः HTTP कीप-लाइव से बहुत लाभ नहीं करता है।

दूसरा यह है कि HttpClient में थ्रॉटलिंग तंत्र नहीं है। वास्तव में यह async ऑपरेशंस से जुड़ी एक सामान्य समस्या प्रतीत होती है। यदि आपको बहुत बड़ी संख्या में ऑपरेशन करने की आवश्यकता है, तो वे सभी एक ही बार में शुरू हो जाएंगे और फिर उपलब्ध होने पर उनकी निरंतरता को निष्पादित किया जाएगा। सिद्धांत रूप में यह ठीक होना चाहिए, क्योंकि एसिंक्रोनस ऑपरेशन में लोड बाहरी प्रणालियों पर होता है लेकिन जैसा कि ऊपर साबित हुआ है यह पूरी तरह से मामला नहीं है। बड़ी संख्या में अनुरोध एक बार शुरू होने से मेमोरी का उपयोग बढ़ेगा और संपूर्ण निष्पादन धीमा हो जाएगा।

मैं एक सरल, लेकिन प्रारंभिक विलंब तंत्र के साथ अतुल्यकालिक अनुरोधों की अधिकतम संख्या को सीमित करके, बेहतर परिणाम, स्मृति और निष्पादन के समय को प्राप्त करने में कामयाब रहा:

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);
    }
}

यह वास्तव में उपयोगी होगा यदि HttpClient समवर्ती अनुरोधों की संख्या को सीमित करने के लिए एक तंत्र शामिल करता है। टास्क क्लास का उपयोग करते समय (जो कि .Net थ्रेड पूल पर आधारित है) थ्रॉटलिंग स्वचालित रूप से समवर्ती धागे की संख्या को सीमित करके प्राप्त किया जाता है।

संपूर्ण अवलोकन के लिए, मैंने HttpWebRequest के आधार पर Async परीक्षण का एक संस्करण भी बनाया है बजाय HttpClient के और बेहतर परिणाम प्राप्त करने में कामयाब रहा। एक शुरुआत के लिए, यह समवर्ती कनेक्शन की संख्या पर एक सीमा निर्धारित करने की अनुमति देता है (ServicePointManager.DefaultConnectionLimit या config के माध्यम से), जिसका अर्थ है कि यह बंदरगाहों से कभी नहीं भागा और किसी भी अनुरोध पर विफल नहीं हुआ (HttpClient, डिफ़ॉल्ट रूप से, HttpWebRequest पर आधारित है) , लेकिन यह कनेक्शन सीमा सेटिंग की उपेक्षा करता है)।

Async HttpWebRequest दृष्टिकोण अभी भी मल्टीथ्रेडिंग की तुलना में लगभग 50 - 60% धीमा था, लेकिन यह अनुमानित और विश्वसनीय था। इसका एकमात्र नकारात्मक पहलू यह था कि इसमें बड़े लोड के तहत भारी मात्रा में मेमोरी का उपयोग किया गया था। उदाहरण के लिए 1 मिलियन अनुरोध भेजने के लिए इसे लगभग 1.6 GB की आवश्यकता थी। समवर्ती अनुरोधों की संख्या को सीमित करके (जैसे मैंने HttpClient के लिए ऊपर किया था) मैं इस्तेमाल की गई मेमोरी को सिर्फ 20 एमबी तक कम करने में कामयाब रहा और मल्टीथ्रेडिंग दृष्टिकोण की तुलना में सिर्फ 10% धीमी गति से निष्पादन का समय प्राप्त किया।

इस लम्बी प्रस्तुति के बाद, मेरे प्रश्न हैं: क्या .net 4.5 से HttpClient वर्ग गहन लोड अनुप्रयोगों के लिए एक बुरा विकल्प है? क्या इसे थ्रॉटल करने का कोई तरीका है, जिसके बारे में मुझे बताई गई समस्याओं को ठीक करना चाहिए? कैसे HttpWebRequest के async स्वाद के बारे में?

अपडेट (धन्यवाद @ स्टीफन क्ली)

जैसा कि यह पता चलता है, HttpWebRequest (जिस पर यह डिफ़ॉल्ट रूप से आधारित है) की तरह, HttpClient, उसी तरह के समवर्ती कनेक्शनों की संख्या ServicePointManager.DefaultConnectionLitit के साथ सीमित हो सकती है। अजीब बात यह है कि MSDN के अनुसार , कनेक्शन सीमा के लिए डिफ़ॉल्ट मान 2 है। मैंने यह भी जाँच की कि डिबगर का उपयोग करते हुए मेरी तरफ से बताया गया है कि वास्तव में 2 डिफ़ॉल्ट मान है। हालाँकि, ऐसा लगता है कि जब तक स्पष्ट रूप से ServicePointManager.DefaultConnectionLimit का मान सेट नहीं किया जाता है, तब तक डिफ़ॉल्ट मान को अनदेखा किया जाएगा। चूंकि मैंने अपने HttpClient परीक्षणों के दौरान स्पष्ट रूप से इसके लिए कोई मूल्य निर्धारित नहीं किया था, इसलिए मुझे लगा कि इसे अनदेखा किया गया है।

ServicePointManager.DefaultConnectionLimit को 100 HttpClient पर सेट करने के बाद विश्वसनीय और पूर्वानुमेय हो गया (netstat पुष्टि करता है कि केवल 100 पोर्ट का उपयोग किया जाता है)। यह अभी भी async HttpWebRequest (लगभग 40%) की तुलना में धीमा है, लेकिन अजीब तरह से, यह कम मेमोरी का उपयोग करता है। परीक्षण के लिए जिसमें 1 मिलियन अनुरोध शामिल हैं, यह async HttpWebbequest में 1.6 GB की तुलना में, अधिकतम 550 MB का उपयोग करता है।

तो, जबकि संयोजन में HttpClient ServicePointManager.DefaultConnectionLimit विश्वसनीयता सुनिश्चित करने के लिए लगता है (कम से कम उस परिदृश्य के लिए जहां सभी कॉल एक ही होस्ट की ओर किए जा रहे हैं), यह अभी भी ऐसा लगता है कि एक उचित थ्रॉटलिंग तंत्र की कमी से इसका प्रदर्शन नकारात्मक रूप से प्रभावित होता है। ऐसा कुछ जो एक विन्यास मूल्य के लिए अनुरोधों की समवर्ती संख्या को सीमित करेगा और बाकी को एक कतार में रखेगा, यह उच्च स्केलेबिलिटी परिदृश्यों के लिए अधिक उपयुक्त होगा।


4
HttpClientसम्मान करना चाहिए ServicePointManager.DefaultConnectionLimit
स्टीफन क्लीयर

2
आपकी टिप्पणियों की जांच के लायक लगता है। एक बात मुझे परेशान कर रही है: मुझे लगता है कि यह एक बार में हजारों एसिंक्स आईओएस जारी करने के लिए बहुत अधिक वंचित है। मैं उत्पादन में ऐसा कभी नहीं करूंगा। तथ्य यह है कि आप async हैं इसका मतलब यह नहीं है कि आप विभिन्न संसाधनों का उपभोग करने वाले पागल हो सकते हैं। (माइक्रोसॉफ़्ट के आधिकारिक नमूने उस संबंध में भी थोड़े भ्रामक हैं।)
usr

1
हालांकि, समय की देरी के साथ थ्रॉटल न करें। एक निश्चित संगामिति स्तर पर थ्रॉटल करें जिसे आप आनुभविक रूप से निर्धारित करते हैं। एक सरल समाधान होगा SemaphoreSlim.WaitAsync हालांकि यह भी मनमाने ढंग से बड़ी मात्रा में कार्यों के लिए उपयुक्त नहीं होगा।
usr

1
@FlorinDumitrescu थ्रॉटलिंग के लिए, आप SemaphoreSlimपहले से बताए गए, या ActionBlock<T>TPL डेटाफ़्लो से उपयोग कर सकते हैं ।
svick

1
@svick, आपके सुझाव के लिए धन्यवाद। मुझे थ्रॉटलिंग / कंसीलर की सीमा के लिए एक तंत्र को मैन्युअल रूप से लागू करने में कोई दिलचस्पी नहीं है। जैसा कि उल्लेख किया गया है, मेरे प्रश्न में शामिल कार्यान्वयन केवल परीक्षण और एक सिद्धांत को मान्य करने के लिए था। मैं इसे बेहतर बनाने की कोशिश नहीं कर रहा हूं, क्योंकि इसका उत्पादन नहीं होगा। अगर मुझे इसमें दिलचस्पी है, तो .Net फ्रेमवर्क एक बिल्ट इन async IO ऑपरेशंस (HttpClient शामिल) की संगति को सीमित करने के लिए एक तंत्र प्रदान करता है।
फ्लोरिन डुमिट्रेस्कु

जवाबों:


64

प्रश्न में उल्लिखित परीक्षणों के अलावा, मैंने हाल ही में कुछ नए HTTP कॉल शामिल किए हैं जिनमें बहुत कम HTTP कॉल (पहले 1 मिलियन की तुलना में 5000) थे, लेकिन उन अनुरोधों पर जिन्हें निष्पादित करने में अधिक समय लगा (500 मिलीसेकंड पहले 1 मिलीसेकंड की तुलना में)। दोनों परीक्षक अनुप्रयोग, सिंक्रोनस मल्टीथ्रेडेड एक (HttpWebRequest पर आधारित) और एसिंक्रोनस I / O एक (HTTP क्लाइंट पर आधारित) समान परिणाम उत्पन्न करते हैं: लगभग 10 सेकंड में लगभग 3% CPU और 30 MB मेमोरी का उपयोग करके निष्पादित करने के लिए। दोनों परीक्षकों के बीच एकमात्र अंतर यह था कि मल्टीथ्रेडेड ने निष्पादित करने के लिए 310 थ्रेड्स का उपयोग किया, जबकि अतुल्यकालिक केवल 22।

मेरे परीक्षणों के निष्कर्ष के रूप में, अतुल्यकालिक HTTP कॉल बहुत तेज़ अनुरोधों से निपटने के दौरान सबसे अच्छा विकल्प नहीं हैं। इसके पीछे का कारण यह है कि जब कोई कार्य जिसमें एक एसिंक्रोनस I / O कॉल होता है, तो जिस थ्रेड पर कार्य प्रारंभ किया जाता है, उसे छोड़ दिया जाता है जैसे ही एसिंक्रोनस कॉल किया जाता है और शेष कार्य कॉलबैक के रूप में पंजीकृत होता है। फिर, जब I / O ऑपरेशन पूरा होता है, कॉलबैक पहले उपलब्ध थ्रेड पर निष्पादन के लिए कतारबद्ध है। यह सब एक उपरि बनाता है, जो तेजी से I / O संचालन को और अधिक कुशल बनाता है जब उन्हें शुरू करने वाले धागे पर निष्पादित किया जाता है।

एसिंक्रोनस HTTP कॉल लंबे या संभावित रूप से लंबे I / O संचालन के साथ काम करते समय एक अच्छा विकल्प है क्योंकि यह I / O संचालन के पूरा होने के इंतजार में किसी भी धागे को व्यस्त नहीं रखता है। यह अनुप्रयोग द्वारा उपयोग किए जाने वाले थ्रेड्स की कुल संख्या को घटाता है, जो सीपीयू बाउंड संचालन द्वारा अधिक सीपीयू समय खर्च करने की अनुमति देता है। इसके अलावा, उन अनुप्रयोगों पर जो केवल थ्रेड की एक सीमित संख्या को आवंटित करते हैं (जैसे कि वेब अनुप्रयोगों के मामले में), अतुल्यकालिक I / O थ्रेड पूल थ्रेड को कम करने से रोकता है, जो कि I / O कॉल को समान रूप से करने पर हो सकता है।

इसलिए, async HttpClient गहन लोड अनुप्रयोगों के लिए एक अड़चन नहीं है। यह सिर्फ इतना है कि इसकी प्रकृति से यह बहुत तेजी से HTTP अनुरोधों के लिए बहुत अच्छी तरह से अनुकूल नहीं है, इसके बजाय यह लंबे या संभावित रूप से लंबे लोगों के लिए आदर्श है, विशेष रूप से अनुप्रयोगों के अंदर जिनके पास सीमित संख्या में थ्रेड उपलब्ध हैं। इसके अलावा, ServicePointManager.DefaultConnectionLimit के माध्यम से संगामिति को सीमित करने के लिए यह एक अच्छा अभ्यास है जो उच्च स्तर के समानता को सुनिश्चित करने के लिए पर्याप्त है, लेकिन अल्पकालिक पोर्ट कमी को रोकने के लिए काफी कम है। आप परीक्षण और निष्कर्ष इस प्रश्न के लिए प्रस्तुत बारे में अधिक जानकारी पा सकते हैं यहाँ


3
"बहुत तेज" कितना तेज है? 1ms? 100ms? 1,000ms?
टिम पी।

मैं विंडोज पर तैनात WebLogic वेब सर्वर पर एक लोड को फिर से खेलना करने के लिए आपके "async" दृष्टिकोण की तरह कुछ का उपयोग कर रहा हूं, लेकिन मुझे जल्दी से नहीं बल्कि रासायनिक बंदरगाह कमी का मुद्दा मिल रहा है। मैंने ServicePointManager.DefaultConnectionLimit को नहीं छुआ है, और मैं प्रत्येक अनुरोध पर सब कुछ (HttpClient और प्रतिक्रिया) का निपटान और पुनः निर्माण कर रहा हूं। क्या आपके पास कोई विचार है कि क्या कनेक्शन खुले रहने के कारण हो सकता है, और बंदरगाहों को खाली कर सकता है?
इरावनची

@TimP। मेरे परीक्षणों के लिए, जैसा कि ऊपर बताया गया है, "बहुत तेज़" अनुरोध थे जिन्हें पूरा करने के लिए सिर्फ 1 मिलीसेकंड का समय लग रहा था। वास्तविक दुनिया में यह हमेशा व्यक्तिपरक होगा। मेरे दृष्टिकोण से, स्थानीय नेटवर्क डेटाबेस पर एक छोटी सी क्वेरी के समतुल्य कुछ तेजी से माना जा सकता है, जबकि इंटरनेट पर एपीआई कॉल के बराबर कुछ को धीमा या संभावित रूप से धीमा माना जा सकता है।
फ्लोरिन डुमिट्रेस्कु

1
@Iravanchi, "async" दृष्टिकोण में, अनुरोध भेजने और प्रतिक्रिया से निपटने के लिए अलग-अलग प्रदर्शन किए जाते हैं। यदि आपके पास बहुत सारे कॉल हैं, तो सभी अनुरोध बहुत तेजी से भेजे जाएंगे और उनके आने पर प्रतिक्रियाएं नियंत्रित की जाएंगी। चूंकि आप उनके जवाब आने के बाद ही कनेक्शन का निपटान कर सकते हैं, इसलिए बड़ी संख्या में समवर्ती कनेक्शन आपके अल्पकालिक बंदरगाहों को जमा और ख़त्म कर सकते हैं। आपको ServicePointManager.DefaultConnectionLimit का उपयोग करके समवर्ती कनेक्शन की अधिकतम संख्या को सीमित करना चाहिए।
फ्लोरिन डुमिट्रेस्कु

1
@FlorinDumitrescu, मैं यह भी जोड़ूंगा कि नेटवर्क कॉल प्रकृति द्वारा अप्रत्याशित हैं। उस समय जब नेटवर्क संसाधन भीड़भाड़ वाला हो या 10% समय में अन्य 10% अनुपलब्ध हो, तो 10ms 90% समय में चलने वाली चीजें अवरोधक मुद्दे पैदा कर सकती हैं।
टिम पी।

27

आपके परिणामों को प्रभावित करने वाली एक बात यह है कि HttpWebRequest के साथ आपको ResponseStream नहीं मिल रहा है और उस स्ट्रीम का उपभोग कर रहा है। HttpClient के साथ, डिफ़ॉल्ट रूप से यह नेटवर्क स्ट्रीम को मेमोरी स्ट्रीम में कॉपी कर देगा। उसी तरह से HttpClient का उपयोग करने के लिए जिस समय आप HttpWebRquest का उपयोग कर रहे हैं, उसी तरह से

var requestMessage = new HttpRequestMessage() {RequestUri = URL};
Task<HttpResponseMessage> getTask = httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead);

दूसरी बात यह है कि मैं वास्तव में निश्चित नहीं हूं कि वास्तविक अंतर क्या है, एक थ्रेडिंग परिप्रेक्ष्य से, आप वास्तव में परीक्षण कर रहे हैं। यदि आप HttpClientHandler की गहराइयों में खुदाई करते हैं, तो यह एक Async अनुरोध करने के लिए बस Task.Factory.StartNew करता है। थ्रेडिंग व्यवहार को सिंक्रनाइज़ेशन संदर्भ में ठीक उसी तरह से प्रस्तुत किया जाता है जैसे कि HttpWebRequest उदाहरण के साथ आपका उदाहरण है।

निस्संदेह, HttpClient डिफ़ॉल्ट रूप से कुछ ओवरहेड जोड़ देता है क्योंकि यह अपने परिवहन पुस्तकालय के रूप में HttpWebRequest का उपयोग करता है। तो आप HttpWebRequest के साथ सीधे HttpClientHandler का उपयोग करते हुए हमेशा बेहतर प्रदर्शन प्राप्त कर पाएंगे। HttpClient लाने के लाभ, HttpResponseMessage, HttpRequestMessage, HttpContent और सभी दृढ़ता से टाइप किए गए हेडर जैसे मानक वर्गों के साथ है। अपने आप में यह एक पूर्ण अनुकूलन नहीं है।


(पुराना उत्तर, लेकिन) HttpClientका उपयोग करना आसान लगता है और मुझे लगता है कि अतुल्यकालिक जाने का रास्ता था, लेकिन इसके आसपास कई "लेकिन और अगर" प्रतीत होते हैं। शायद HttpClientफिर से लिखा जाना चाहिए ताकि यह उपयोग करने के लिए अधिक सहज हो? या कि प्रलेखन वास्तव में महत्वपूर्ण चीजों पर जोर दे रहा था कि इसे सबसे अधिक कुशलता से कैसे उपयोग किया जाए?
मोर्टब

@mortb, Flurl.Http flurl.io HttpClient के उपयोग के आवरण के लिए एक अधिक सहज है
माइकल Freidgeim

1
@MichaelFreidgeim: धन्यवाद, हालांकि मैंने अब तक HttpClient के साथ रहना सीख लिया है ...
मोर्टब

17

हालांकि यह सीधे ओपी के प्रश्न के 'एसिंक्स' भाग का जवाब नहीं देता है, यह उस कार्यान्वयन में त्रुटि को संबोधित करता है जिसका वह उपयोग कर रहा है।

यदि आप अपने एप्लिकेशन को स्केल करना चाहते हैं, तो उदाहरण-आधारित HttpClients का उपयोग करने से बचें। अंतर बहुत बड़ा है! लोड के आधार पर, आपको बहुत भिन्न प्रदर्शन संख्याएँ दिखाई देंगी। HttpClient अनुरोधों को फिर से उपयोग करने के लिए डिज़ाइन किया गया था। इसकी पुष्टि बीसीएल टीम के लोगों ने की, जिन्होंने इसे लिखा था।

हाल ही में एक परियोजना मुझे ब्लैक फ्राइडे / कुछ नए सिस्टम के लिए छुट्टी यातायात के लिए एक बहुत बड़े और प्रसिद्ध ऑनलाइन कंप्यूटर रिटेलर स्केल की मदद करने के लिए मिली थी। हम HttpClient के उपयोग के आसपास कुछ प्रदर्शन मुद्दों में भाग गए। चूंकि यह लागू होता है IDisposable, देवताओं ने वह किया जो आप आमतौर पर एक उदाहरण बनाकर और एक using()बयान के अंदर रखकर करेंगे । एक बार जब हमने ऐप को लोड करना शुरू कर दिया तो ऐप ने सर्वर को अपने घुटनों पर ला दिया - हाँ, सर्वर न केवल ऐप। कारण यह है कि HttpClient के प्रत्येक उदाहरण में सर्वर पर एक I / O पूर्णता पोर्ट खुलता है। जीसी के गैर-निर्धारक अंतिमकरण और इस तथ्य के कारण कि आप कंप्यूटर संसाधनों के साथ काम कर रहे हैं जो कि कई ओएसआई परतों में फैला हुआ है , नेटवर्क पोर्ट को बंद करने में कुछ समय लग सकता है। वास्तव में विंडोज ओएस हीएक पोर्ट (प्रति Microsoft) को बंद करने के लिए 20 सेकंड तक का समय लग सकता है। पोर्ट बंद होने से हम तेजी से पोर्ट खोल रहे थे - सर्वर पोर्ट थकावट जिसने सीपीयू को 100% तक सीमित कर दिया। मेरा फिक्स HttpClient को एक स्थिर उदाहरण में बदलना था जिसने समस्या को हल किया। हां, यह एक डिस्पोजेबल संसाधन है, लेकिन किसी भी ओवरहेड को प्रदर्शन के अंतर से बहुत अधिक प्रभावित किया जाता है। मैं आपको यह देखने के लिए प्रोत्साहित करता हूं कि आपका ऐप कैसे व्यवहार करता है, यह देखने के लिए कुछ लोड टेस्टिंग करें।

नीचे दिए गए लिंक पर भी जवाब दिया:

WebAPI क्लाइंट में प्रति कॉल एक नया HttpClient बनाने का ओवरहेड क्या है?

https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client


मुझे क्लाइंट पर टीसीपी पोर्ट थकावट पैदा करने वाला एक ही मुद्दा मिला है। समाधान लंबे समय के लिए HttpClient उदाहरण को पट्टे पर देना था जहां पुनरावृत्तियां की जा रही थीं, प्रत्येक कॉल के लिए निर्माण और निपटान नहीं। मैं जिस निष्कर्ष पर पहुँचा था, वह "बस इसलिए कि यह डिस्पोज़ को लागू करता है, इसका मतलब यह नहीं है कि इसका डिस्पोज़ करना सस्ता है"।
फिलिप

इसलिए यदि HttpClient स्थिर है, और मुझे अगले अनुरोध पर हेडर बदलने की आवश्यकता है, तो वह पहले अनुरोध का क्या करता है? HttpClient को बदलने के बाद से स्थिर होने में कोई नुकसान नहीं है - जैसे कि HttpClient.DefaultRequestHeaders.Accept.Clear () जारी करना; ? उदाहरण के लिए, यदि मेरे पास ऐसे उपयोगकर्ता हैं जो टोकन के माध्यम से प्रमाणित कर रहे हैं, तो उन टोकन को एपीआई के अनुरोध पर हेडर के रूप में जोड़ा जाना चाहिए, जिनमें से भिन्न टोकन हैं। HttpClient के स्थैतिक होने और फिर HttpClient पर इस हेडर को बदलने से प्रतिकूल प्रभाव नहीं पड़ेगा?
23

यदि आपको HttpClient उदाहरण के सदस्यों जैसे हेडर / कुकीज़, आदि का उपयोग करने की आवश्यकता है, तो आपको एक स्थिर HttpClient का उपयोग नहीं करना चाहिए। अन्यथा, आपका उदाहरण डेटा (हेडर, कुकीज) हर अनुरोध के लिए समान होगा - निश्चित रूप से वह नहीं जो आप चाहते हैं।
डेव ब्लैक

चूँकि यह मामला है ... आप अपनी पोस्ट में ऊपर जो भी वर्णन कर रहे हैं उसे कैसे रोकेंगे - लोड के खिलाफ? बाल्कन लोड करें और उस पर अधिक सर्वर फेंकें?
crizzwald

@crizzwald - मेरे पोस्ट में मैंने उपयोग किए गए समाधान को नोट किया। HttpClient के स्थिर उदाहरण का उपयोग करें। यदि आपको किसी HttpClient पर हेडर / कुकीज़ का उपयोग करने की आवश्यकता है, तो मैं एक विकल्प का उपयोग करना चाहूंगा।
डेव ब्लैक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.