ASP.NET वेब API के साथ async / wait का प्रभावी रूप से उपयोग करें


114

मैं async/awaitअपने वेब एपीआई प्रोजेक्ट में ASP.NET की सुविधा का उपयोग करने की कोशिश कर रहा हूं । मुझे बहुत यकीन नहीं है कि इससे मेरी वेब एपीआई सेवा के प्रदर्शन में कोई फर्क पड़ेगा। कृपया मेरे आवेदन से वर्कफ़्लो और नमूना कोड के नीचे खोजें।

कार्य प्रवाह:

UI एप्लिकेशन → वेब एपीआई एंडपॉइंट (नियंत्रक) → वेब एपीआई सेवा परत में कॉल विधि → किसी अन्य बाहरी वेब सेवा को कॉल करें। (यहाँ हम DB बातचीत, आदि)

नियंत्रक:

public async Task<IHttpActionResult> GetCountries()
{
    var allCountrys = await CountryDataService.ReturnAllCountries();

    if (allCountrys.Success)
    {
        return Ok(allCountrys.Domain);
    }

    return InternalServerError();
}

सेवा परत:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");

    return Task.FromResult(response);
}

मैंने उपरोक्त कोड का परीक्षण किया और काम कर रहा है। लेकिन मुझे यकीन नहीं है कि यह सही उपयोग है async/await। कृपया अपने विचार साझा करें।


जवाबों:


202

मुझे बहुत यकीन नहीं है कि इससे मेरे एपीआई के प्रदर्शन पर कोई फर्क पड़ेगा।

यह ध्यान रखें कि सर्वर साइड पर एसिंक्रोनस कोड का प्राथमिक लाभ स्केलेबिलिटी है । यह जादुई रूप से आपके अनुरोधों को तेज़ी से नहीं चलाएगा। मैं ASP.NET परasync अपने लेखasync में कई "मुझे उपयोग करना चाहिए " विचारों को शामिल किया गया है

मुझे लगता है कि आपका उपयोग मामला (अन्य एपीआई को कॉल करना) अतुल्यकालिक कोड के लिए अच्छी तरह से अनुकूल है, बस ध्यान रखें कि "अतुल्यकालिक" का अर्थ "तेज" नहीं है। सबसे अच्छा तरीका यह है कि पहले अपने UI को उत्तरदायी और अतुल्यकालिक बनाया जाए; यह थोड़ा धीमा होने पर भी आपके ऐप को तेज़ महसूस कराएगा।

जहां तक ​​कोड जाता है, यह अतुल्यकालिक नहीं है:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
  var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
  return Task.FromResult(response);
}

स्केलेबिलिटी लाभ प्राप्त करने के लिए आपको वास्तव में अतुल्यकालिक कार्यान्वयन की आवश्यकता होगी async:

public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

या (यदि इस पद्धति में आपका तर्क वास्तव में सिर्फ पास-थ्रू है):

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

ध्यान दें कि इस तरह से "बाहर" के बजाय "अंदर बाहर" से काम करना आसान है। दूसरे शब्दों में, एक अतुल्यकालिक नियंत्रक कार्रवाई के साथ शुरू नहीं करते हैं और फिर डाउनस्ट्रीम विधियों को अतुल्यकालिक होने के लिए मजबूर करते हैं। इसके बजाय, स्वाभाविक रूप से अतुल्यकालिक संचालन (बाहरी एपीआई, डेटाबेस प्रश्नों, आदि को कॉल करना) की पहचान करें, और उन अतुल्यकालिक को पहले निम्नतम स्तर ( Service.ProcessAsync) पर बनाएं । तो चलोasync अंतिम चरण के रूप में अपने नियंत्रक कार्यों को अतुल्यकालिक बनाते हुए, ट्रिकल चालू करें।

और किसी भी परिस्थिति में आपको Task.Runइस परिदृश्य में उपयोग नहीं करना चाहिए ।


4
धन्यवाद स्टीफन आपकी बहुमूल्य टिप्पणियों के लिए। मैंने अपने सभी सर्विस लेयर के तरीकों को async में बदल दिया है और अब मैं ExecuteTaskAsync विधि का उपयोग करके अपने बाहरी REST कॉल को कॉल करता हूं और उम्मीद के मुताबिक काम कर रहा हूं। इसके अलावा async और कार्य के बारे में अपने ब्लॉग पोस्ट के लिए धन्यवाद। प्रारंभिक समझ पाने में इससे मुझे बहुत मदद मिली।
एरप जूल

5
क्या आप समझा सकते हैं कि आपने टास्क का इस्तेमाल क्यों नहीं किया।
मार्टन

1
@Maarten: मैं इसे (संक्षेप में) उस लेख में समझाता हूं जिससे मैं जुड़ा हुआ हूं । मैं अपने ब्लॉग पर अधिक विस्तार में जाता हूं
स्टीफन क्लीयर

मैंने आपका लेख स्टीफन पढ़ा है और यह कुछ उल्लेख करने से बचता है। जब ASP.NET अनुरोध आता है और शुरू होता है तो यह थ्रेड पूल थ्रेड पर चल रहा है, यह ठीक है। लेकिन अगर यह async हो जाता है तो हाँ यह async प्रसंस्करण शुरू करता है और वह धागा तुरंत पूल में लौट आता है। लेकिन async विधि में किया गया कार्य स्वयं थ्रेड पूल थ्रेड पर निष्पादित होगा!
ह्यूज


12

यह सही है, लेकिन शायद उपयोगी नहीं है।

जैसा कि प्रतीक्षा करने के लिए कुछ भी नहीं है - एपीआई को अवरुद्ध करने के लिए कोई कॉल नहीं जो असिंक्रोनस रूप से काम कर सकता है - फिर आप असिंक्रोनस ऑपरेशन (जिसमें ओवरहेड) को ट्रैक करने के लिए संरचनाएं स्थापित कर रहे हैं, लेकिन फिर उस क्षमता का उपयोग नहीं कर रहे हैं।

उदाहरण के लिए, यदि सेवा परत इकाई ढांचे के साथ DB संचालन कर रही थी जो अतुल्यकालिक कॉल का समर्थन करता है:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    using (db = myDBContext.Get()) {
      var list = await db.Countries.Where(condition).ToListAsync();

       return list;
    }
}

आप कार्यकर्ता थ्रेड को कुछ और करने की अनुमति देंगे, जबकि db क्वियर किया गया था (और इस तरह एक और अनुरोध को संसाधित करने में सक्षम)।

अद्वैत कुछ ऐसा हो जाता है जिसके लिए सभी तरह से नीचे जाने की आवश्यकता होती है: यह मौजूदा प्रणाली में रेट्रो-फिट करने के लिए बहुत कठिन है।


-1

आप async / leveraging नहीं कर रहे हैं प्रभावी रूप से प्रतीक्षा करें क्योंकि सिंक्रोनस विधि निष्पादित करते समय अनुरोध थ्रेड अवरुद्ध हो जाएगाReturnAllCountries()

किसी अनुरोध को संभालने के लिए जो थ्रेड असाइन किया गया ReturnAllCountries()है वह काम करते समय पूरी तरह से प्रतीक्षा कर रहा होगा ।

यदि आप ReturnAllCountries()अतुल्यकालिक होने के लिए लागू कर सकते हैं , तो आप स्केलेबिलिटी लाभ देखेंगे। ऐसा इसलिए है क्योंकि ReturnAllCountries()निष्पादन के दौरान थ्रेड को .NET थ्रेड पूल में वापस भेजा जा सकता है, जबकि निष्पादन हो रहा है। यह थ्रेड्स का अधिक कुशलता से उपयोग करके आपकी सेवा को उच्चतर थ्रूपुट बनाने की अनुमति देगा।


यह सच नहीं है। सॉकेट जुड़ा हुआ है लेकिन अनुरोध को संसाधित करने वाला थ्रेड कुछ और कर सकता है, जैसे कि सेवा कॉल पर प्रतीक्षा करते समय एक अलग अनुरोध संसाधित करता है।
गैर गॉडफ्रे

-8

मैं आपकी सेवा परत को इसमें बदलूंगा:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    return Task.Run(() =>
    {
        return _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
    }      
}

जैसा कि आपके पास है, आप अभी भी अपने _service.Processकॉल को सिंक्रोनाइज़ कर रहे हैं , और इसे प्राप्त करने से बहुत कम या कोई लाभ नहीं मिल रहा है।

इस दृष्टिकोण के साथ, आप संभावित धीमी कॉल को एक में लपेट रहे हैं Task, इसे शुरू कर रहे हैं, और इसे प्रतीक्षा करने के लिए वापस कर रहे हैं। अब आपको प्रतीक्षा करने का लाभ मिलेगा Task


3
ब्लॉग लिंक के अनुसार , मैं देख सकता था कि भले ही हम टास्क का उपयोग करते हैं।
एआरपी

हम्म। उपरोक्त कोड और उस लिंक के बीच बहुत अंतर हैं। आपका Serviceएक एस्किंस्ट विधि नहीं है, और Serviceकॉल के भीतर ही प्रतीक्षा नहीं की जा रही है। मैं उनकी बात समझ सकता हूं, लेकिन मुझे विश्वास नहीं है कि यह यहां लागू होता है।
जोन्सोपोलिस जूल

8
Task.RunASP.NET पर टाला जाना चाहिए। यह दृष्टिकोण async/ से सभी लाभों को हटा देगा awaitऔर वास्तव में केवल एक तुल्यकालिक कॉल की तुलना में लोड के तहत खराब प्रदर्शन करेगा।
स्टीफन क्लीयर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.