क्या HttpClient और HttpClientHandler को अनुरोधों के बीच निपटाया जाना है?


334

System.Net.Http.HttpClient और System.Net.Http.HttpClientHandler .NET फ्रेमवर्क 4.5 में IDis प्रयोज्य को लागू करें ( System.Net.Http.Http.MessageInvoker के माध्यम से )।

usingबयान प्रलेखन का कहना है:

एक नियम के रूप में, जब आप एक आइडीएसोपॉलिज्ड ऑब्जेक्ट का उपयोग करते हैं, तो आपको एक उपयोग स्टेटमेंट में इसे घोषित और तत्काल करना चाहिए।

यह उत्तर इस पैटर्न का उपयोग करता है:

var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("foo", "bar"),
        new KeyValuePair<string, string>("baz", "bazinga"),
    });
    cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
    var result = client.PostAsync("/test", content).Result;
    result.EnsureSuccessStatusCode();
}

लेकिन Microsoft से सबसे अधिक दिखाई देने वाले उदाहरण कॉल नहीं करते हैं Dispose() स्पष्ट रूप से या अंतर्निहित रूप से । उदाहरण के लिए:

में घोषणा की टिप्पणियों है, किसी से कहा कि माइक्रोसॉफ्ट कर्मचारी:

आपके नमूनों की जाँच करने के बाद, मैंने देखा कि आपने HttpClient उदाहरण पर डिस्पोज़ करने की क्रिया नहीं की। मैंने अपने ऐप पर स्टेटमेंट का उपयोग करने के साथ HttpClient के सभी उदाहरणों का उपयोग किया है और मैंने सोचा कि यह सही तरीका है क्योंकि HttpClient आईडीआईसोफ़ेक्टिव इंटरफ़ेस को लागू करता है। क्या मैं सही रास्ते पर हूँ?

उनका जवाब था:

सामान्य तौर पर यह सही है, हालांकि आपको "उपयोग" और async से सावधान रहना होगा क्योंकि वे वास्तव में '.net 4' में मिश्रण नहीं करते हैं, .Net 4.5 आप "का उपयोग कर" कथन के अंदर "इंतजार" का उपयोग कर सकते हैं।

Btw, आप एक ही HttpClient को कई बार फिर से उपयोग कर सकते हैं [जैसा कि] आपको पसंद है इसलिए आमतौर पर आप उन्हें हर समय नहीं बनाएंगे / उन्हें डिस्पोज़ नहीं करेंगे।

दूसरा पैराग्राफ इस सवाल के लिए बहुत ही अच्छा है, जो इस बात से चिंतित नहीं है कि आप कितनी बार HttpClient इंस्टेंस का उपयोग कर सकते हैं, लेकिन अगर इसके बाद आपको इसकी आवश्यकता नहीं है, तो इसे निपटाने के लिए आवश्यक है।

(अपडेट: वास्तव में दूसरा पैराग्राफ उत्तर की कुंजी है, जैसा कि @DPeden द्वारा नीचे दिया गया है।)

तो मेरे सवाल हैं:

  1. HttpClient और HttpClientHandler उदाहरणों पर Dispose () कॉल करने के लिए क्या यह आवश्यक है कि वर्तमान कार्यान्वयन (.NET फ्रेमवर्क 4.5) दिया जाए? स्पष्टीकरण: "आवश्यक" से मेरा मतलब है कि यदि निपटान नहीं करने के लिए कोई नकारात्मक परिणाम हैं, जैसे कि संसाधन रिसाव या डेटा भ्रष्टाचार जोखिम।

  2. यदि यह आवश्यक नहीं है, तो क्या यह वैसे भी "अच्छा अभ्यास" होगा, क्योंकि वे आईडीआईएसओपी को लागू करते हैं?

  3. यदि यह आवश्यक (या अनुशंसित) है, तो क्या यह कोड सुरक्षित रूप से लागू करने के लिए ऊपर उल्लिखित है (.NET फ्रेमवर्क 4.5 के लिए)?

  4. यदि इन वर्गों को डिसपोजल () कॉल करने की आवश्यकता नहीं है, तो उन्हें आईडीसोप्लिक के रूप में क्यों लागू किया गया?

  5. यदि उन्हें आवश्यकता है, या यदि यह एक अनुशंसित अभ्यास है, तो क्या Microsoft उदाहरण भ्रामक या असुरक्षित हैं?


2
@Damien_The_Unbeliever, आपकी प्रतिक्रिया के लिए धन्यवाद। क्या आपके पास कोई सुझाव है कि मैं प्रश्न को कैसे स्पष्ट कर सकता हूं? मैं जानना चाहता हूं कि क्या यह आम तौर पर संसाधनों के निपटान से जुड़े मुद्दों को जन्म दे सकता है, जैसे कि संसाधन रिसाव और डेटा भ्रष्टाचार।
फर्नांडो कोर्रेया

9
@Damien_The_Unbeliever: सच नहीं है। विशेष रूप से, धारा लेखकों को सही व्यवहार करने के लिए निपटाया जाना चाहिए।
स्टीफन क्लीयर

1
@StephenCleary - आप किन पहलुओं के बारे में सोच रहे हैं? निश्चित रूप से, आप Flushप्रत्येक लिखने के बाद एक पर कॉल कर सकते हैं , और इसके अलावा असुविधा के अलावा अंतर्निहित संसाधनों को अधिक से अधिक समय तक जारी रखना आवश्यक है, "सही व्यवहार" के लिए क्या आवश्यक नहीं होगा?
डेमियन_इन_अनबेलियर

1
यह स्पष्ट रूप से गलत है: "एक नियम के रूप में, जब आप एक आइडीएसोपोलाईट ऑब्जेक्ट का उपयोग करते हैं, तो आपको एक घोषित स्टेटमेंट में इसे घोषित और त्वरित करना चाहिए"। मैं हमेशा यह तय करने से पहले कि क्या मुझे इसके लिए उपयोग करना चाहिए, यह तय करने से पहले आईडीसोपायरी को लागू करने वाले वर्ग पर प्रलेखन पढ़ें। पुस्तकालयों के लेखक के रूप में जहां मैं आइडीएसोपॉलिटि रेज़्यूएज़ को लागू करता हूं, उन्हें अनमैंग्ड रिसोर्सेस जारी करने की आवश्यकता होती है, अगर उपभोक्ताओं ने एक मौजूदा इंस्टेंस का फिर से उपयोग करने के बजाय प्रत्येक बार एक उदाहरण का निपटारा किया तो मैं भयभीत हो जाऊंगा। यह नहीं कहना है कि अंत में उदाहरण के लिए नहीं है ..
markmnl

1
मैंने अपने डॉक्स को अपडेट करने के लिए microsoft में PR सबमिट किया है: github.com/dotnet/docs/pull/2470
markmnl

जवाबों:


259

आम सहमति यह है कि आपको HttpClient को निपटाने की आवश्यकता नहीं है (नहीं करना चाहिए)।

बहुत से लोग जो अंतरंग रूप से इसमें काम करते हैं, उन्होंने यह कहा है।

देखें डेरेल मिलर के ब्लॉग पोस्ट और एक संबंधित एसओ पोस्ट: स्मृति रिसाव में HttpClient रेंगने परिणाम संदर्भ के लिए।

मैं यह भी दृढ़ता से सुझाव देता हूं कि आप हुड के नीचे क्या चल रहा है, विशेष रूप से "लाइफसाइकल" अनुभाग के संदर्भ में ASP.NET के साथ डिजाइनिंग अयोग्य वेब APIs से HttpClient अध्याय पढ़ें ।

यद्यपि HttpClient अप्रत्यक्ष रूप से IDisposable इंटरफ़ेस को लागू करता है, लेकिन HttpClient का मानक उपयोग हर अनुरोध के बाद इसे निपटाने के लिए नहीं है। HttpClient ऑब्जेक्ट को तब तक जीवित रहने का इरादा है जब तक आपके आवेदन को HTTP अनुरोध करने की आवश्यकता होती है। कई अनुरोधों में एक वस्तु मौजूद होने से DefaultRequestHeaders को सेट करने के लिए एक जगह सक्षम हो जाती है और आपको HttpWebRequest के साथ आवश्यक होने पर हर अनुरोध पर क्रेडेंशियल कैश और कुकीसींटर जैसी चीजों को फिर से निर्दिष्ट करने से रोकता है।

या यहां तक ​​कि DotPeek खोलें।


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

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

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

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

19
@DavidPeden एक एकल के रूप में HttpClient को पंजीकृत करना मेरे लिए खतरनाक लगता है क्योंकि यह परिवर्तनशील है। उदाहरण के लिए, क्या Timeoutसंपत्ति को आवंटित करने वाले सभी लोग एक-दूसरे पर दबाव नहीं डालेंगे?
जॉन-एरिक

47

वर्तमान उत्तर थोड़े भ्रमित और भ्रामक हैं, और वे कुछ महत्वपूर्ण डीएनएस निहितार्थ याद कर रहे हैं। मैं संक्षेप में बताने की कोशिश करूंगा कि चीजें स्पष्ट रूप से कहां खड़ी हैं।

  1. आम तौर पर अधिकांश IDisposableवस्तुओं को बोलते समय आदर्श रूप से निपटाया जाना चाहिए जब आप उनके साथ काम करते हैं , विशेष रूप से उन लोगों के पास जिनके नाम / साझा ओएस संसाधन हैंडारेल मिलर केHttpClient रूप में कोई अपवाद नहीं है बताते हैं कि यह रद्द टोकन आवंटित करता है, और अनुरोध / प्रतिक्रिया निकायों को अप्रबंधित धाराएं हो सकती हैं।
  2. हालांकि, HttpClient के लिए सबसे अच्छा अभ्यास यह कहता है कि आपको एक उदाहरण बनाना चाहिए और इसे जितना संभव हो उतना पुन: उपयोग करना चाहिए ( बहु-थ्रेडेड परिदृश्यों में इसके थ्रेड-सुरक्षित सदस्यों का उपयोग करके )। इसलिए, अधिकांश परिदृश्यों में आप इसका निपटान कभी नहीं करेंगे, क्योंकि आपको हर समय इसकी आवश्यकता होगी
  3. एक ही HttpClient "हमेशा के लिए" का उपयोग करने के साथ समस्या यह है कि अंतर्निहित HTTP कनेक्शन मूल रूप से DNS- हल किए गए IP के खिलाफ खुला रह सकता है, भले ही DNS परिवर्तन हो । यह ब्लू / ग्रीन परिनियोजन और DNS- आधारित विफलता जैसे परिदृश्यों में एक समस्या हो सकती है । इस समस्या से निपटने के लिए विभिन्न दृष्टिकोण हैं, सबसे विश्वसनीय जिसमें Connection:closeDNS परिवर्तन होने के बाद हेडर भेजना शामिल है। एक अन्य संभावना में HttpClientक्लाइंट की ओर से समय-समय पर या कुछ तंत्र के माध्यम से रीसाइक्लिंग होता है जो DNS परिवर्तन के बारे में सीखता है। अधिक जानकारी के लिए https://github.com/dotnet/corefx/issues/11224 देखें (लिंक ब्लॉग पोस्ट में सुझाए गए कोड का नेत्रहीन उपयोग करने से पहले मैं इसे ध्यान से पढ़ने का सुझाव देता हूं)।

मैं इसे हर समय
निपटाता

यदि आपको किसी भी कारण से HttpClient को निपटाने की आवश्यकता है, तो आपको HttpMessageHandler के आस-पास एक स्थिर उदाहरण रखना चाहिए, क्योंकि यह निपटाना कि वास्तव में HttpClient को निपटाने के लिए जिम्मेदार मुद्दों का कारण है। HttpClient में एक कंस्ट्रक्टर ओवरलोड है जो आपको यह निर्दिष्ट करने की अनुमति देता है कि आपूर्ति किए गए हैंडलर को निपटाया नहीं जाना चाहिए, जिस स्थिति में आप HttpMessageHandler को अन्य HttpClient इंस्टेंस के साथ पुन: उपयोग कर सकते हैं।
टॉम लिंट

आपको अपने HttpClient पर पकड़ करनी चाहिए, लेकिन आप System.Net.ServicePointManager.DnsRefreshTimeout = 3000 जैसे कुछ का उपयोग कर सकते हैं; यह उपयोगी है जैसे कि यदि आप मोबाइल डिवाइस पर हैं तो किसी भी समय वाईफाई और 4 जी के बीच स्विच कर सकते हैं।
जोहान फ्रैंकन

18

मेरी समझ में, कॉलिंग Dispose()केवल तभी आवश्यक है जब यह संसाधनों को लॉक करने की आवश्यकता हो जो आपको बाद में चाहिए (जैसे एक विशेष कनेक्शन)। यह हमेशा उन संसाधनों को मुक्त करने के लिए अनुशंसित है जिनका आप अब उपयोग नहीं कर रहे हैं, भले ही आपको उन्हें फिर से ज़रूरत न हो, बस इसलिए कि आप आम तौर पर नहीं होना चाहिए उन संसाधनों पर पकड़ रख रहे हैं जिनका आप उपयोग नहीं कर रहे हैं (दंडित उद्देश्य)।

Microsoft उदाहरण गलत नहीं है, जरूरी है। आवेदन से बाहर निकलने पर उपयोग किए जाने वाले सभी संसाधन जारी किए जाएंगे। और उस उदाहरण के मामले में, ऐसा होने के लगभग तुरंत बाद होता HttpClientहै। मामलों की तरह, स्पष्ट रूप से बुला रहा हैDispose() कुछ हद तक शानदार है।

लेकिन, सामान्य तौर पर, जब कोई वर्ग लागू होता है IDisposable, तो समझ यह है कि Dispose()जैसे ही आप पूरी तरह से तैयार और सक्षम होते हैं, आपको उसके उदाहरणों पर ध्यान देना चाहिए । मैं चाहता हूं कि यह उन मामलों में विशेष रूप से सच है जैसे कि HttpClientयह स्पष्ट रूप से प्रलेखित नहीं है कि क्या संसाधन या कनेक्शन चालू / खुले हैं। उस मामले में जहां कनेक्शन का फिर से उपयोग किया जाएगा [जल्द ही], आप इसे प्राप्त करना चाहते हैं Dipose()- आप उस मामले में "पूरी तरह से तैयार" नहीं हैं।

इन्हें भी देखें: IDisposable.Dispose मेथड और कब कॉल करें डिस्पोज


7
यह ऐसा है जैसे कोई आपके घर पर केला लाता है, उसे खाता है और छिलके के साथ खड़ा होता है। उन्हें छिलके के साथ क्या करना चाहिए? ... अगर वे इसके साथ दरवाजे से बाहर जाते हैं, तो उन्हें जाने दें। यदि वे चारों ओर चिपक रहे हैं, तो उन्हें कचरे में फेंक दें ताकि यह जगह को बदबू न दे।
svidgen

बस इस उत्तर को स्पष्ट करने के लिए, क्या आप कह रहे हैं, "यह जरूरी नहीं है कि यदि आप इसे इस्तेमाल करने के बाद कार्यक्रम को समाप्त करने जा रहे हैं तो इसे निपटाना जरूरी नहीं है"? और अगर आप कार्यक्रम को कुछ समय के लिए जारी रखने की अपेक्षा करते हैं तो आपको निपटाना चाहिए?
फर्नांडो कोर्रेया

@FernandoCorreia हाँ, जब तक मैं कुछ नहीं भूल रहा हूँ, मुझे लगता है कि यह एक सुरक्षित सिद्धांत है। हालांकि यह प्रत्येक मामले में कुछ सोचा दे। यदि आप एक कनेक्शन के साथ काम कर रहे हैं, उदाहरण के लिए, आप Dispose()इसे समय से पहले नहीं चाहते हैं और कुछ सेकंड बाद फिर से कनेक्ट करना होगा यदि मौजूदा कनेक्शन पुन: प्रयोज्य है। इसी तरह, आप अनावश्यक रूप Dispose()से उन छवियों या अन्य संरचनाओं को नहीं चाहते जिन्हें आप अंत में एक या दो मिनट में पुनर्निर्माण कर सकते हैं।
svidgen

मै समझता हुँ। लेकिन HttpClient और HttpClientHandler के विशेष मामले में यह सवाल है कि क्या वे एक HTTP कनेक्शन के रूप में इस तरह का एक संसाधन खोल रहे हैं? यदि ऐसा हो रहा है, तो मुझे उनके उपयोग के अपने पैटर्न पर पुनर्विचार करना पड़ सकता है।
फर्नांडो कोर्रेया

1
@DPeden मेरा उत्तर मेरा विरोध करने में बिल्कुल नहीं है। ध्यान दें, मैंने कहा, जैसे ही आप पूरी तरह तैयार और सक्षम हों , आपको इसके उदाहरणों का निपटान () करना चाहिए । यदि आप फिर से उदाहरण का उपयोग करने की योजना बनाते हैं, तो आप तैयार नहीं हैं ।
svidgen

9

डिस्पोज़ () नीचे दिए गए कोड को कॉल करता है, जो HttpClient उदाहरण द्वारा खोले गए कनेक्शन को बंद कर देता है। कोड को dotPeek के साथ विघटित करके बनाया गया था।

HttpClientHandler.cs - निपटान

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);

यदि आप निपटान नहीं कहते हैं, तो ServicePointManager.MaxServicePointIdleTime, जो एक टाइमर से चलता है, http कनेक्शन बंद कर देगा। डिफ़ॉल्ट 100 सेकंड है।

ServicePointManager.cs

internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(ServicePointManager.IdleServicePointTimeoutCallback);
private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100000);

private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
{
  ServicePoint servicePoint = (ServicePoint) context;
  if (Logging.On)
    Logging.PrintInfo(Logging.Web, SR.GetString("net_log_closed_idle", (object) "ServicePoint", (object) servicePoint.GetHashCode()));
  lock (ServicePointManager.s_ServicePointTable)
    ServicePointManager.s_ServicePointTable.Remove((object) servicePoint.LookupString);
  servicePoint.ReleaseAllConnectionGroups();
}

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


1
आप github पर कोड भी देख सकते हैं। github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/…
TamusJRoyce

8

संक्षिप्त उत्तर: नहीं, वर्तमान में स्वीकृत उत्तर में कथन सही नहीं है : "सामान्य सर्वसम्मति यह है कि आपको HttpClient को निपटाने की आवश्यकता नहीं है (नहीं करना चाहिए")।

दीर्घ उत्तर : एक ही समय में निम्नलिखित कथनों में से प्रत्येक सत्य और प्राप्य है:

  1. "HttpClient का इरादा एक बार तत्काल किया जाना है और एक आवेदन के जीवन भर में फिर से उपयोग किया जाता है", आधिकारिक दस्तावेज से उद्धृत ।
  2. एक IDisposableवस्तु को माना / अनुशंसित किया जाना है।

और वे एक-दूसरे के साथ परस्पर संबंध नहीं रखते हैं। यह केवल एक बात है कि आप अपने कोड को पुन: उपयोग करने के लिए कैसे व्यवस्थित करते हैंHttpClient और फिर भी इसे ठीक से निपटाने के लिए।

मेरे दूसरे उत्तर से उद्धृत एक लंबा जवाब :

यह में लोगों को देखने के लिए एक संयोग नहीं है कुछ ब्लॉग पोस्ट पर आरोप लगा है कि कैसे HttpClientके IDisposableइंटरफेस उन्हें इस्तेमाल करते हैं बनाता है using (var client = new HttpClient()) {...}पैटर्न और फिर थक सॉकेट हैंडलर समस्या को जन्म दे।

मेरा मानना ​​है कि एक अनिर्दिष्ट (गलत?) गर्भाधान के लिए नीचे आता है: "एक IDisposable वस्तु के अल्पकालिक होने की उम्मीद है"

जब हम इस शैली में कोड लिखते हैं तो यह निश्चित रूप से एक अल्पकालिक चीज की तरह दिखता है:

using (var foo = new SomeDisposableObject())
{
    ...
}

IDisposable पर आधिकारिक दस्तावेज कभी नहीं का उल्लेख हैIDisposable वस्तुओं अल्पकालिक रहना होगा। परिभाषा के अनुसार, आईडीसॉरोप्लेट केवल एक तंत्र है जो आपको अप्रबंधित संसाधनों को जारी करने की अनुमति देता है। और कुछ नहीं। उस अर्थ में, आप अंततः निपटान को ट्रिगर करने के लिए तैयार हैं, लेकिन यह आपको अल्पकालिक फैशन में ऐसा करने की आवश्यकता नहीं है।

इसलिए आपका काम ठीक से चुनना है जब निपटान को ट्रिगर करना है, तो अपनी वास्तविक वस्तु के जीवन चक्र की आवश्यकता के आधार पर। लंबे समय तक जीवित रहने के तरीके में आपको आईडीसोफ्री का उपयोग करने से कोई रोक नहीं सकता है:

using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");

            using (var client = new HttpClient())
            {
                for (...) { ... }  // A really long loop

                // Or you may even somehow start a daemon here

            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

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


इस स्पष्टीकरण के लिए धन्यवाद। यह स्पष्ट रूप से आम सहमति की ओर कुछ प्रकाश डाल रहा है। आपकी राय से, आपको कब कॉल करना उचित है HttpClient.Dispose?
जेसन मार्तज्या

@JesonMartajaya, जब आपके एप्लिकेशन को httpClient उदाहरण का उपयोग करने की आवश्यकता नहीं है, तो इसे डिस्पोज़ करें। आपको लगता है कि ऐसा सुझाव अस्पष्ट लग सकता है, लेकिन वास्तव में यह आपके HttpClient clientचर के जीवन चक्र के साथ पूरी तरह से संरेखित हो सकता है , जो कि एक प्रोग्रामिंग -१०१ बात है जिसे आप पहले से ही वैसे भी कर रहे हैं। आप अभी भी उपयोग करने में सक्षम हो सकते using (...) {...}हैं। उदाहरण के लिए, मेरे उत्तर के अंदर हैलो वर्ल्ड का नमूना देखें।
रायलू

7

चूंकि यह प्रकट नहीं होता है कि किसी को भी इसे यहाँ अभी तक का उल्लेख किया गया है, नई सबसे अच्छा तरीका नेट कोर 2.1 में प्रबंधन करने के लिए HttpClient और HttpClientHandler उपयोग कर रहा है HttpClientFactory

यह एक साफ और आसान तरीके से उपर्युक्त मुद्दों और gotchas के अधिकांश हल करती है। से स्टीव गॉर्डन का शानदार ब्लॉग पोस्ट :

अपने .Net Core (2.1.1 या बाद वाले) प्रोजेक्ट में निम्नलिखित पैकेज जोड़ें:

Microsoft.AspNetCore.All
Microsoft.Extensions.Http

इसे Startup.cs में जोड़ें:

services.AddHttpClient();

इंजेक्शन और उपयोग:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IHttpClientFactory _httpClientFactory;

    public ValuesController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    [HttpGet]
    public async Task<ActionResult> Get()
    {
        var client = _httpClientFactory.CreateClient();
        var result = await client.GetStringAsync("http://www.google.com");
        return Ok(result);
    }
}

कई और विशेषताओं के लिए स्टीव के ब्लॉग में पदों की श्रृंखला का अन्वेषण करें।


4

मेरे मामले में, मैं एक तरीके के अंदर एक HttpClient बना रहा था जिसने वास्तव में सेवा कॉल किया था। कुछ इस तरह:

public void DoServiceCall() {
  var client = new HttpClient();
  await client.PostAsync();
}

एक Azure कार्यकर्ता भूमिका में, इस पद्धति को बार-बार कॉल करने के बाद (HttpClient का निपटान किए बिना), यह अंततः SocketException(कनेक्शन प्रयास विफल) के साथ विफल हो जाएगा ।

मैंने HttpClient को एक इंस्टेंस वेरिएबल बनाया (इसे क्लास के स्तर पर डिस्पोज करना) और इश्यू चला गया। तो मैं कहूँगा, हाँ, HttpClient को निपटाना, ऐसा करने के लिए अपनी सुरक्षित (आपके पास बकाया async कॉल नहीं है)।


प्रतिक्रिया के लिए धन्यवाद। यह कुछ हद तक जटिल मुद्दा है। मैं DPeden के जवाब में जुड़े लेखों को पढ़ने की सलाह देता हूं। संक्षेप में, HttpClient उदाहरण को पूरे जीवनचक्र में पुन: उपयोग किया जाना चाहिए। यदि आप बार-बार नए उदाहरण बनाते हैं, तो आपको उनका निपटान करने की आवश्यकता हो सकती है।
फर्नांडो कोर्रेया

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

@nashwan आप हेडर को साफ़ नहीं कर सकते हैं और प्रत्येक अनुरोध से पहले नए जोड़ सकते हैं?
मनदीप जंजुआ

Microsoft भी HttpClient उदाहरणों का पुनः उपयोग करने की सिफारिश कर रहा है - docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/…
मंदीप जंजुआ

@MandeepJanjua कि उदाहरण एक सांत्वना अनुप्रयोग के रूप में एक ग्राहक प्रतीत होता है। मैं एक वेब एप्लिकेशन के क्लाइंट होने का जिक्र कर रहा था।
बाईटदेव

3

विशिष्ट उपयोग (प्रतिक्रियाओं <2GB) में HttpResponseMessages को निपटाना आवश्यक नहीं है।

यदि उनकी स्ट्रीम सामग्री पूरी तरह से पढ़ें नहीं है, तो HttpClient विधियों के वापसी प्रकारों को छोड़ दिया जाना चाहिए। अन्यथा सीएलआर के पास उन धाराओं को जानने का कोई तरीका नहीं है जब तक कि वे कचरा एकत्र नहीं किए जाते।

  • यदि आप डेटा को बाइट में पढ़ रहे हैं [] (जैसे GetByteArrayAsync) या स्ट्रिंग, तो सभी डेटा पढ़ा जाता है, इसलिए डिस्पोज़ करने की कोई आवश्यकता नहीं है।
  • अन्य अधिभार 2GB तक स्ट्रीम पढ़ने के लिए डिफ़ॉल्ट होंगे (HttpCompletionOption रिस्पॉन्सकंटेंट रीड, HttpClient.MaxResponseContentBufferSize डिफ़ॉल्ट 2GB है)

यदि आप HttpCompletionOption को ResponseHeadersRead पर सेट करते हैं या प्रतिक्रिया 2GB से बड़ी है, तो आप सफाई कर सकते हैं। यह HttpResponseMessage पर कॉल करके या HttpResonseMessage सामग्री से प्राप्त स्ट्रीम पर Dispose / Close को कॉल करके या पूरी तरह से सामग्री को पढ़कर किया जा सकता है।

चाहे आप HttpClient पर Dispose कहते हैं, यह निर्भर करता है कि आप लंबित अनुरोधों को रद्द करना चाहते हैं या नहीं।


2

यदि आप HttpClient का निपटान करना चाहते हैं, तो आप इसे संसाधन पूल के रूप में सेट कर सकते हैं। और आपके आवेदन के अंत में, आप अपने संसाधन पूल का निपटान करते हैं।

कोड:

// Notice that IDisposable is not implemented here!
public interface HttpClientHandle
{
    HttpRequestHeaders DefaultRequestHeaders { get; }
    Uri BaseAddress { get; set; }
    // ...
    // All the other methods from peeking at HttpClient
}

public class HttpClientHander : HttpClient, HttpClientHandle, IDisposable
{
    public static ConditionalWeakTable<Uri, HttpClientHander> _httpClientsPool;
    public static HashSet<Uri> _uris;

    static HttpClientHander()
    {
        _httpClientsPool = new ConditionalWeakTable<Uri, HttpClientHander>();
        _uris = new HashSet<Uri>();
        SetupGlobalPoolFinalizer();
    }

    private DateTime _delayFinalization = DateTime.MinValue;
    private bool _isDisposed = false;

    public static HttpClientHandle GetHttpClientHandle(Uri baseUrl)
    {
        HttpClientHander httpClient = _httpClientsPool.GetOrCreateValue(baseUrl);
        _uris.Add(baseUrl);
        httpClient._delayFinalization = DateTime.MinValue;
        httpClient.BaseAddress = baseUrl;

        return httpClient;
    }

    void IDisposable.Dispose()
    {
        _isDisposed = true;
        GC.SuppressFinalize(this);

        base.Dispose();
    }

    ~HttpClientHander()
    {
        if (_delayFinalization == DateTime.MinValue)
            _delayFinalization = DateTime.UtcNow;
        if (DateTime.UtcNow.Subtract(_delayFinalization) < base.Timeout)
            GC.ReRegisterForFinalize(this);
    }

    private static void SetupGlobalPoolFinalizer()
    {
        AppDomain.CurrentDomain.ProcessExit +=
            (sender, eventArgs) => { FinalizeGlobalPool(); };
    }

    private static void FinalizeGlobalPool()
    {
        foreach (var key in _uris)
        {
            HttpClientHander value = null;
            if (_httpClientsPool.TryGetValue(key, out value))
                try { value.Dispose(); } catch { }
        }

        _uris.Clear();
        _httpClientsPool = null;
    }
}

var हैंडलर = HttpClientHander.GetHttpClientHandle (नई उड़ी ("बेस यूआरएल"))।

  • HttpClient, एक इंटरफ़ेस के रूप में, डिस्पोज़ () नहीं कह सकता।
  • कचरा कलेक्टर द्वारा डिसपोजल () को विलंबित फैशन में कहा जाएगा। या जब कार्यक्रम अपने विध्वंसक के माध्यम से वस्तु को साफ करता है।
  • कमजोर संदर्भों का उपयोग करता है + विलंबित सफाई तर्क का उपयोग करता है इसलिए यह इतने लंबे समय तक उपयोग में रहता है।
  • यह केवल प्रत्येक आधार URL के लिए एक नया HttpClient आवंटित करता है जो इसे पास किया गया है। नीचे ओहड श्नाइडर द्वारा बताए गए कारण। बेस यूआरएल बदलते समय बुरा व्यवहार।
  • HttpClientHandle परीक्षणों में मॉकिंग की अनुमति देता है

उत्तम। मैं देखता हूं कि आप उस Disposeविधि पर कॉल करते हैं जिसे आप GC पर रजिस्टर करते हैं। यह शीर्ष पर उच्च दर्जा दिया जाना चाहिए।
जेसन मार्तजया

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

1

अपने कंस्ट्रक्टर में निर्भरता इंजेक्शन का उपयोग करना आपके जीवनकाल को HttpClientआसान बनाता है - जीवन भर के प्रबंधन को उस कोड के बाहर ले जाना जो इसकी आवश्यकता है और बाद की तारीख में इसे आसानी से बदलने योग्य बनाता है।

मेरी वर्तमान प्राथमिकता एक अलग http क्लाइंट क्लास बनाना है जो HttpClientएक बार लक्ष्य समापन बिंदु डोमेन से विरासत में मिला है और फिर निर्भरता इंजेक्शन का उपयोग करके इसे एक सिंगलटन बनाता है।public class ExampleHttpClient : HttpClient { ... }

तब मैं सेवा http में कस्टम http क्लाइंट पर एक कंस्ट्रक्टर निर्भरता लेता हूं जहां मुझे उस एपीआई तक पहुंच की आवश्यकता होती है। यह जीवनकाल की समस्या को हल करता है और फायदे तब होता है जब यह कनेक्शन पूलिंग के लिए आता है।

आप संबंधित जवाब में काम किया हुआ उदाहरण https://stackoverflow.com/a/50238944/3140853 पर देख सकते हैं


0

कृपया नीचे दिए गए एक बहुत ही समान प्रश्न के मेरे उत्तर पर पढ़ें। यह स्पष्ट होना चाहिए कि आपको HttpClientउदाहरणों को एकल के रूप में मानना ​​चाहिए और अनुरोधों पर फिर से उपयोग किया जाना चाहिए ।

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


-2

मुझे लगता है कि किसी को HttpClient के उदाहरण बनाने और इसे हर समय बंद करने से बचने के लिए सिंगलटन पैटर्न का उपयोग करना चाहिए। यदि आप .Net 4.0 का उपयोग कर रहे हैं, तो आप नीचे दिए गए नमूने कोड का उपयोग कर सकते हैं। सिंगलटन पैटर्न की अधिक जानकारी के लिए यहां देखें

class HttpClientSingletonWrapper : HttpClient
{
    private static readonly Lazy<HttpClientSingletonWrapper> Lazy= new Lazy<HttpClientSingletonWrapper>(()=>new HttpClientSingletonWrapper()); 

    public static HttpClientSingletonWrapper Instance {get { return Lazy.Value; }}

    private HttpClientSingletonWrapper()
    {
    }
}

नीचे दिए गए कोड का उपयोग करें।

var client = HttpClientSingletonWrapper.Instance;

3
ऐसा करने के लिए कुछ देखने के लिए (और अन्य समान योजनाओं): " किसी भी उदाहरण के सदस्यों को धागा सुरक्षित होने की गारंटी नहीं है। "
tne

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