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


162

HttpClientएक WebAPI क्लाइंट का जीवनकाल कितना होना चाहिए ?
क्या इसका एक उदाहरण होना बेहतर हैHttpClient कई कॉल लिए ?

HttpClientप्रति अनुरोध बनाने और निपटाने का ओवरहेड क्या है , जैसे नीचे उदाहरण में ( http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from- से लिया गया है) एक शुद्ध-ग्राहक ):

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("http://localhost:9000/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    // New code:
    HttpResponseMessage response = await client.GetAsync("api/products/1");
    if (response.IsSuccessStatusCode)
    {
        Product product = await response.Content.ReadAsAsync<Product>();
        Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
    }
}

मुझे यकीन नहीं है, आप Stopwatchइसे बेंचमार्क करने के लिए क्लास का उपयोग कर सकते हैं , हालाँकि। मेरा अनुमान यह होगा कि एकल होना अधिक मायने रखता है HttpClient, यह मानते हुए कि उन सभी उदाहरणों को एक ही संदर्भ में उपयोग किया जाता है।
मैथ्यू

जवाबों:


215

HttpClientको कई कॉल के लिए फिर से उपयोग करने के लिए डिज़ाइन किया गया है । यहां तक ​​कि कई धागों में भी। HttpClientHandlerसाख और कुकीज़ है कि पुन: उपयोग किए कॉल भर में करने का इरादा कर रहे हैं। एक नया HttpClientउदाहरण होने से उस सभी सामान को फिर से सेट करना पड़ता है। यह भीDefaultRequestHeaders संपत्ति में कई कॉल के लिए इच्छित गुण हैं। प्रत्येक अनुरोध पर उन मानों को रीसेट करने से बिंदु को हराया जाता है।

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

जितना अधिक आप सुविधाओं का उपयोग करते हैं HttpClient, उतना ही आप देखेंगे कि मौजूदा उदाहरण का पुन: उपयोग करना समझ में आता है।

हालांकि, मेरी राय में, सबसे बड़ा मुद्दा यह है कि जब एक HttpClientवर्ग का निपटान किया जाता है, तो वह निपटता है HttpClientHandler, जो तब जबरन उन TCP/IPकनेक्शनों के पूल में कनेक्शन को बंद कर देता है जो उसके द्वारा प्रबंधित किया जाता है ServicePointManager। इसका मतलब यह है कि नए के साथ प्रत्येक अनुरोध को HttpClientनए TCP/IPकनेक्शन को फिर से स्थापित करने की आवश्यकता होती है ।

मेरे परीक्षणों से, LAN पर सादे HTTP का उपयोग करके, प्रदर्शन हिट काफी नगण्य है। मुझे इस पर संदेह है क्योंकि एक अंतर्निहित टीसीपी रखवाली है जो HttpClientHandlerइसे बंद करने की कोशिश करने पर भी कनेक्शन खुला रखती है।

इंटरनेट पर जाने के अनुरोध पर, मैंने एक अलग कहानी देखी है। मैंने हर बार अनुरोध को दोबारा खोलने के कारण 40% प्रदर्शन देखा है।

मुझे लगता है कि एक HTTPSकनेक्शन पर हिट और भी बदतर होगी।

मेरी सलाह है कि प्रत्येक विशिष्ट एपीआई के लिए अपने एप्लिकेशन के जीवनकाल के लिए HttpClient का एक उदाहरण रखें, जिससे आप कनेक्ट होते हैं।


5
which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManagerआप इस कथन के बारे में कैसे सुनिश्चित हैं? ऐसा विश्वास करना कठिन है। HttpClientमुझे एक यूनिट-ऑफ-वर्क की तरह लगता है जिसे अक्सर इंस्टेंट किया जाना चाहिए।
usr

2
@vkelman हां आप अभी भी HttpClient के एक उदाहरण का पुन: उपयोग कर सकते हैं, भले ही आपने इसे नए HttpClientHandler के साथ बनाया हो। यह भी ध्यान दें कि HttpClient के लिए एक विशेष निर्माणकर्ता है जो आपको HttpClientHandler का पुन: उपयोग करने और कनेक्शन को मारने के बिना HttpClient को निपटाने की अनुमति देता है।
डारेल मिलर

2
@vkelman मैं HttpClient को चारों ओर रखना पसंद करता हूं, लेकिन यदि आप HttpClientHandler को चारों ओर रखना पसंद करते हैं, तो यह दूसरा पैरामीटर गलत होने पर कनेक्शन को खुला रखेगा।
डारेल मिलर

2
@DarrelMiller तो ऐसा लगता है कि कनेक्शन HttpClientHandler से जुड़ा है। मुझे पता है कि पैमाने पर मैं कनेक्शन को नष्ट नहीं करना चाहता, इसलिए मुझे या तो एक HttpClientHandler को अपने पास रखना होगा और उस से मेरे सभी HttpClient उदाहरण बनाने होंगे या एक स्थिर HttpClient उदाहरण बनाना होगा। हालाँकि, अगर CookContainer HttpClientHandler के लिए बाध्य है, और मेरे कुकीज़ को प्रति अनुरोध अलग करने की आवश्यकता है, तो आप क्या सलाह देते हैं? मैं एक स्थिर HttpClientHandler पर थ्रेड सिंक्रोनाइज़ेशन से बचना चाहूंगा।
डेव ब्लैक

2
@ साना। ९ १। सेवा संग्रह में एक सिंगलटन के रूप में इसे पंजीकृत करना और इसे इस तरह एक्सेस करना बेहतर होगा।
डारेल मिलर

69

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

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

आप प्रलेखन और उदाहरण के लिए WebAPI दिशानिर्देश पृष्ठ https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client पर देख सकते हैं

इस कॉल-आउट पर विशेष ध्यान दें:

HttpClient को एक बार इंस्टेंटिएट करने और एप्लिकेशन के पूरे जीवन में फिर से उपयोग करने का इरादा है। विशेष रूप से सर्वर अनुप्रयोगों में, हर अनुरोध के लिए एक नया HttpClient उदाहरण बनाने से भारी भार के तहत उपलब्ध सॉकेट्स की संख्या समाप्त हो जाएगी। इसके परिणामस्वरूप सॉकेटएक्सपेप्शन त्रुटियां होंगी।

यदि आप पाते हैं कि आपको HttpClientअलग-अलग हेडर, आधार पते, आदि के साथ एक स्टेटिक का उपयोग करने की आवश्यकता है , तो आपको HttpRequestMessageमैन्युअल रूप से बनाने और उन मानों को सेट करने की आवश्यकता है HttpRequestMessage। फिर, का उपयोग करेंHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)

.NET कोर के लिए अद्यतन : आपको उदाहरण IHttpClientFactoryबनाने के HttpClientलिए डिपेंडेंसी इंजेक्शन के माध्यम से उपयोग करना चाहिए । यह आपके लिए जीवनकाल का प्रबंधन करेगा और आपको इसे स्पष्ट रूप से निपटाने की आवश्यकता नहीं है। ASP.NET कोर में IHttpClientFactory का उपयोग करके HTTP अनुरोध देखें


1
इस पोस्ट में उन लोगों के लिए उपयोगी अंतर्दृष्टि है जो तनाव परीक्षण करेंगे ..!
सना ..91

9

जैसा कि अन्य जवाब राज्य, HttpClientपुन: उपयोग के लिए है। हालाँकि, HttpClientबहु-थ्रेडेड अनुप्रयोग में किसी एकल उदाहरण का पुन: उपयोग करने का अर्थ है कि आप इसके राज्य-योग्य गुणों के मूल्यों को नहीं बदल सकते हैं, जैसे BaseAddressऔर DefaultRequestHeaders(इसलिए आप केवल उनका उपयोग कर सकते हैं यदि वे आपके आवेदन भर में स्थिर हैं)।

इस सीमा के आस-प्राप्त करने के लिए एक दृष्टिकोण रैपिंग है HttpClientएक वर्ग है कि सभी डुप्लिकेट के साथ HttpClient(तरीकों की जरूरत है GetAsync, PostAsyncआदि) और उन्हें प्रतिनिधियों एक सिंगलटन करने के लिए HttpClient। हालांकि यह बहुत थकाऊ है (आपको विस्तार विधियों को भी लपेटने की आवश्यकता होगी ), और सौभाग्य से एक और तरीका है - नए HttpClientउदाहरण बनाते रहें , लेकिन अंतर्निहित का पुन: उपयोग करें HttpClientHandler। बस सुनिश्चित करें कि आप हैंडलर का निपटान न करें:

HttpClientHandler _sharedHandler = new HttpClientHandler(); //never dispose this
HttpClient GetClient(string token)
{
    //client code can dispose these HttpClient instances
    return new HttpClient(_sharedHandler, disposeHandler: false)         
    {
       DefaultRequestHeaders = 
       {
            Authorization = new AuthenticationHeaderValue("Bearer", token) 
       } 
    };
}

2
जाने का बेहतर तरीका है कि एक HttpClient उदाहरण रखें, और फिर अपना स्वयं का स्थानीय HttpRequestMessage इंस्टेंसेस बनाएं और फिर HttpClient पर .SendAsync () विधि का उपयोग करें। इस तरह यह अभी भी थ्रेड-सेफ होगा। प्रत्येक HttpRequestMessage का अपना प्रमाणीकरण / URL मान होगा।
टिम पी।

@TimP। यह बेहतर क्यों है? SendAsyncसमर्पित विधियों की तुलना में बहुत कम सुविधाजनक है PutAsync, PostAsJsonAsyncजैसे
Ohad Schneider

2
SendAsync आइए आप URL और अन्य गुणों जैसे हेडर और फिर भी थ्रेड-सुरक्षित रहें।
टिम पी।

2
हां, हैंडलर कुंजी है। जब तक कि HttpClient उदाहरणों के बीच साझा किया जाता है आप ठीक हैं। मैंने आपकी पहले की टिप्पणी को गलत बताया।
डेव ब्लैक

1
यदि हम एक साझा हैंडलर रखते हैं, तो क्या हमें अभी भी बासी DNS मुद्दे का ध्यान रखना चाहिए?
शांति

5

उच्च-वॉल्यूम वेब साइटों से संबंधित है लेकिन सीधे HttpClient के लिए नहीं। हमारे पास हमारी सभी सेवाओं में नीचे दिए गए कोड के स्निपेट हैं।

        // number of milliseconds after which an active System.Net.ServicePoint connection is closed.
        const int DefaultConnectionLeaseTimeout = 60000;

        ServicePoint sp =
                ServicePointManager.FindServicePoint(new Uri("http://<yourServiceUrlHere>"));
        sp.ConnectionLeaseTimeout = DefaultConnectionLeaseTimeout;

से https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Net.ServicePoint.ConnectionLeaseTimeout);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2); कश्मीर (DevLang-कोई तिथि नहीं) & rd सच =

"आप इस संपत्ति का उपयोग यह सुनिश्चित करने के लिए कर सकते हैं कि सर्विसपॉइंट ऑब्जेक्ट का सक्रिय कनेक्शन अनिश्चित काल तक खुला न रहे। यह गुण उन परिदृश्यों के लिए अभिप्रेत है जहाँ कनेक्शन को समय-समय पर और पुन: स्थापित किया जाना चाहिए, जैसे लोड संतुलन परिदृश्य।

डिफ़ॉल्ट रूप से, जब KeepAlive किसी अनुरोध के लिए सत्य है, तो MaxIdleTime गुण निष्क्रियता के कारण ServicePoint कनेक्शन को बंद करने के लिए टाइम-आउट सेट करता है। यदि ServicePoint में सक्रिय कनेक्शन हैं, तो MaxIdleTime का कोई प्रभाव नहीं पड़ता है और कनेक्शन अनिश्चित काल तक खुले रहते हैं।

जब ConnectionLeaseTimeout प्रॉपर्टी को -1 के अलावा किसी वैल्यू पर सेट किया जाता है, और निर्दिष्ट समय बीतने के बाद, उस अनुरोध को गलत रखने के लिए KeepAlive सेट करके एक सर्विस सर्विस कनेक्शन को बंद कर दिया जाता है। इस मान को सेट करने से ServicePoint ऑब्जेक्ट द्वारा प्रबंधित सभी कनेक्शन प्रभावित होते हैं। "

जब आपके पास CDN या अन्य समापन बिंदु के पीछे सेवाएँ होती हैं जिन्हें आप विफल करना चाहते हैं, तो यह सेटिंग कॉल करने वालों को आपके नए गंतव्य पर जाने में आपकी सहायता करती है। इस उदाहरण में एक विफलता के 60 सेकंड के बाद सभी कॉलर्स को नए समापन बिंदु से फिर से कनेक्ट होना चाहिए। इसके लिए आवश्यक है कि आप अपनी निर्भर सेवाओं (उन सेवाओं को जिन्हें आप कहते हैं) और उनके समापन बिंदुओं को जानते हैं।


आप अभी भी कनेक्शन खोलकर और बंद करके सर्वर पर बहुत अधिक भार डालते हैं। यदि आप आवृत्ति-आधारित HttpClientHandlers के साथ आवृत्ति-आधारित HttpClients का उपयोग करते हैं, तो आप अभी भी पोर्ट-थकावट में भाग लेंगे यदि आप सावधान नहीं हैं।
डेव ब्लैक

असहमति नहीं। सब कुछ एक व्यापार है। हमारे लिए एक पुन: रूट की गई सीडीएन या डीएनएस के बाद बैंक बनाम खोए हुए राजस्व में पैसा है।
नो रिफंड्स नो रिटर्न

1

आप साइमन टिम द्वारा इस ब्लॉग पोस्ट को संदर्भित करना चाह सकते हैं: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

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


1

एक बात और बताइए, कि "कोई भी उपयोग न करें" ब्लॉग नोट में यह है कि यह सिर्फ बेसअड्रेस और डिफाल्टर नहीं है जिस पर आपको विचार करने की आवश्यकता है। एक बार जब आप HttpClient को स्थिर बनाते हैं, तो आंतरिक स्थिति होती है जिसे अनुरोधों के पार ले जाया जाएगा। एक उदाहरण: आप FedAuth टोकन प्राप्त करने के लिए HttpClient के साथ एक तृतीय पक्ष को प्रमाणित कर रहे हैं (उपेक्षा करें कि OAuth / OWIN / etc का उपयोग क्यों नहीं कर रहे हैं), कि रिस्पांस संदेश में FedAuth के लिए सेट-कुकी शीर्षलेख है, यह आपके HttpClient राज्य में जोड़ा जाता है। आपके एपीआई में प्रवेश करने वाला अगला उपयोगकर्ता अंतिम व्यक्ति की फेडआथ कुकी भेज रहा होगा जब तक कि आप प्रत्येक अनुरोध पर इन कुकीज़ को प्रबंधित नहीं कर रहे हों।


0

पहले मुद्दे के रूप में, जबकि यह वर्ग डिस्पोजेबल है, usingबयान के साथ इसका उपयोग करना सबसे अच्छा विकल्प नहीं है क्योंकि जब आप HttpClientऑब्जेक्ट का निपटान करते हैं , तो अंतर्निहित सॉकेट को तुरंत जारी नहीं किया जाता है और 'सॉकेट्स थकावट' नामक एक गंभीर समस्या पैदा कर सकता है।

लेकिन इसके साथ एक दूसरा मुद्दा HttpClientहै जब आप इसे सिंगलटन या स्टैटिक ऑब्जेक्ट के रूप में उपयोग कर सकते हैं। इस स्थिति में, एक एकल या स्थिर परिवर्तन का HttpClientसम्मान नहीं करता है DNS

, .net कोर में आप HttpClientFactory कुछ इस तरह से कर सकते हैं :

public interface IBuyService
{
    Task<Buy> GetBuyItems();
}
public class BuyService: IBuyService
{
    private readonly HttpClient _httpClient;

    public BuyService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<Buy> GetBuyItems()
    {
        var uri = "Uri";

        var responseString = await _httpClient.GetStringAsync(uri);

        var buy = JsonConvert.DeserializeObject<Buy>(responseString);
        return buy;
    }
}

ConfigureServices

services.AddHttpClient<IBuyService, BuyService>(client =>
{
     client.BaseAddress = new Uri(Configuration["BaseUrl"]);
});

प्रलेखन और उदाहरण यहाँ

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.