2016-03-02 14 views
19

हैंगिंग हैं हमारे पास हमारे समाधान के लिए एसओए है। हम .net फ्रेमवर्क 4.5.1, एएसपीएनटी एमवीसी 4.6, एसक्यूएल सर्वर, विंडोज सर्वर और थिंकटेक्चर पहचान सर्वर 3 (टोकन आधारित वेबपी कॉल के लिए) का उपयोग कर रहे हैं।एमवीसी एप्लीकेशन असिंक तरीके

समाधान संरचना की तरह दिखता है;
enter image description here

हमारा एमवीसी फ्रंटएंड एप्लिकेशन httpClient wrapper के माध्यम से हमारे वेबपी एप्लिकेशन के साथ वार्ता करता है। यहां जेनेरिक http क्लाइंट रैपर कोड है;

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Net; 
using System.Net.Http; 
using System.Net.Http.Headers; 
using System.Threading.Tasks; 

namespace Cheetah.HttpClientWrapper 
{ 
    public class ResourceServerRestClient : IResourceServerRestClient 
    { 
     private readonly ITokenProvider _tokenProvider; 

     public ResourceServerRestClient(ITokenProvider tokenProvider) 
     { 
      _tokenProvider = tokenProvider; 
     } 

     public string BaseAddress { get; set; } 

     public Task<T> GetAsync<T>(string uri, string clientId) 
     { 
      return CheckAndInvokeAsync(async token => 
      { 
       using (var client = new HttpClient()) 
       { 
        ConfigurateHttpClient(client, token, clientId); 

        HttpResponseMessage response = await client.GetAsync(uri); 

        if (response.IsSuccessStatusCode) 
        { 
         return await response.Content.ReadAsAsync<T>(); 
        } 

        var exception = new Exception($"Resource server returned an error. StatusCode : {response.StatusCode}"); 
        exception.Data.Add("StatusCode", response.StatusCode); 
        throw exception; 
       } 
      }); 
     } 

     private void ConfigurateHttpClient(HttpClient client, string bearerToken, string resourceServiceClientName) 
     { 
      if (!string.IsNullOrEmpty(resourceServiceClientName)) 
      { 
       client.DefaultRequestHeaders.Add("CN", resourceServiceClientName); 
      } 

      if (string.IsNullOrEmpty(BaseAddress)) 
      { 
       throw new Exception("BaseAddress is required!"); 
      } 

      client.BaseAddress = new Uri(BaseAddress); 
      client.Timeout = new TimeSpan(0, 0, 0, 10); 
      client.DefaultRequestHeaders.Accept.Clear(); 
      client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); 
     } 

     private async Task<T> CheckAndInvokeAsync<T>(Func<string, Task<T>> method) 
     { 
      try 
      { 
       string token = await _tokenProvider.IsTokenNullOrExpired(); 

       if (!string.IsNullOrEmpty(token)) 
       { 
        return await method(token); 
       } 

       var exception = new Exception(); 
       exception.Data.Add("StatusCode", HttpStatusCode.Unauthorized); 
       throw exception; 
      } 
      catch (Exception ex) 
      { 
       if (ex.Data.Contains("StatusCode") && ((HttpStatusCode)ex.Data["StatusCode"]) == HttpStatusCode.Unauthorized) 
       { 
        string token = await _tokenProvider.GetTokenAsync(); 

        if (!string.IsNullOrEmpty(token)) 
        { 
         return await method(token); 
        } 
       } 

       throw; 
      } 
     } 

     public void ThrowResourceServerException(List<string> messages) 
     { 
      string message = messages.Aggregate((p, q) => q + " - " + p); 

      var exception = new Exception(message); 

      exception.Data.Add("ServiceOperationException", message); 

      throw exception; 
     } 
    } 
} 

इसके अलावा, कभी कभी इस http ग्राहक आवरण NitoAsync प्रबंधक (कॉल सिंक के रूप में async तरीकों।), और कभी कभी हम इस सामान्य विधि सीधे उपयोग कर रहे हैं इंतजार के साथ साथ का उपयोग कर - async कार्य की तरह इंतजार;

जब हम, JMeter के साथ हमारे MVC आवेदन का परीक्षण (कुछ तरह-की लोड परीक्षण/1 सेकंड प्रति 10 धागे बनाने के लिए) कुछ मिनटों के बाद, MVC:

var result = await _resourceServerRestClient.GetAsync<ServiceOperation<DailyAgendaModel>>("dailyAgenda/" + id); 

तो यहाँ हमारी समस्या है एप्लिकेशन इस लाइन पर काम करना बंद कर देता है [अपवाद कार्यकाल के कारण रद्द कर दिया गया है] (शायद केवल 1-2 अनुरोध टाइमआउट): HttpResponseMessage response = await client.GetAsync(uri);। लेकिन उस अनुरोध के बाद, सभी अनुरोध विफल होंगे जैसे कि वे पंक्ति में हैं। तो एमवीसी आवेदन 2-15 मिनट (यादृच्छिक रूप से) के लिए लटक रहा है लेकिन उस समय मैं डाकिया से वेबपीआई में नए अनुरोध भेज सकता हूं। वे ठीक हैं, मेरा मतलब है कि वेबपी अच्छी प्रतिक्रिया दे रहा है। कुछ मिनट के बाद एमवीसी आवेदन सामान्य करने के लिए टर्नबैक।

नोट: हमारे पास एमवीसी-यूई और वेबपी के लिए लोड-बैलेंसर है। क्योंकि कभी-कभी हमें व्यस्त दिन में एक मिनट में 120 के अनुरोध मिलते हैं। लेकिन अगर वेबपै या एमवीसी एप्लीकेशन के सामने कोई लोड बैलेंसर नहीं है तो यह वही त्रुटि देता है। तो यह एलबी समस्या नहीं है।

टिप्पणी 2: हम MVC-ui और WebAPI संचार के लिए RestSharp का उपयोग करने की कोशिश की। हमें यहां एक ही त्रुटि मिली है। जब कोई पुन: प्रयास विफल हो रहा है, तो सभी अनुरोध एक पंक्ति में विफल हो जाएंगे। ऐसा लगता है कि यह एक नेटवर्क त्रुटि है लेकिन हमें इसके लिए सबूत नहीं मिल रहा है।

क्या आप मेरे httpClient wrapper पर कोई त्रुटि देख सकते हैं? या बेहतर सवाल है;
आपके समाधान में, आपका एमवीसी एप्लीकेशन आपके वेबपी एप्लिकेशन के साथ संचार कैसे कर रहा है? यहां सबसे अच्छा अभ्यास क्या हैं?

Update1: हम परियोजनाओं 4.6.1 के लिए 4.5.1 शुद्ध ले जाया गया। वही डेडलॉक फिर से हुआ। और हम अस्थायी रूप से परत के सभी स्रोत कोड स्थानांतरित किए गए: "व्यापार & रिपोजिटरी" डीएलएल स्तर के रूप में। व्यवसाय & प्रस्तुति स्तर के बीच अब कोई वेबपी नहीं है। मृत ताला हल हो गया। हम अभी भी खोज रहे हैं कि httpClientWrapper कोड ठीक से काम नहीं कर रहे हैं जब हमने अपने वेबप्लिकेशन नियंत्रकों से वेबपीआई विधियों को बुलाया।

+0

आपने क्लाइंट साइड अनुरोध कोड साझा किया है। लेकिन मुख्य समस्या वीपी एपीआई है जो सामान्य प्रतिक्रिया नहीं दे सकती है। आपका अनुरोध लटका दिया जाएगा।वेब एपीआई पक्ष अधिक तेज़ होना चाहिए। आप टाइमआउट बढ़ा सकते हैं। मैं इसकी सिफारिश नहीं करता हूं। शायद आप थ्रेड के साथ अपना अनुरोध सो सकते हैं। वे अच्छे समाधान नहीं हैं। कृपया वीपी एपीआई पक्ष साझा करें। शायद हम आपके जवाब के लिए प्रदर्शन –

+0

@ SercanTimoçin धन्यवाद बढ़ा सकते हैं। Howerer webapi सबसे अच्छा एपीआई है जिसे हमने लिखा है, हमारे लोड परीक्षणों में; अधिकतम प्रतिक्रिया समय <500ms है। इसलिए हमें नहीं लगता कि यह वेब एपीआई विधि है। –

+0

आपका सबसे अच्छा शर्त एक पूर्ण मेमोरी डंप करना है जब आपका एप्लिकेशन लटकता/समय समाप्त हो जाता है। आप [WinDbg] (https://msdn.microsoft.com/en-us/windows/hardware/hh852365.aspx) जैसे कुछ का उपयोग कर सकते हैं, फिर 'एनालिज' या [डीबग डायग] चलाएं (https: // www क्या हो रहा है यह देखने के लिए .microsoft.com/en-us/download/details.aspx? id = 49924)। यह हो सकता है कि मृतक या संसाधन जारी नहीं किए जा रहे हों या कौन जानता है कि कोई भी आवेदन कोड देखे बिना और यह कैसे व्यवहार कर रहा है देखे बिना कोई भी कह सकता है। – Igor

उत्तर

0

मैं बिल्कुल यकीन नहीं WHU हूँ, लेकिन मैं GetAsync() विधि

public async Task<T> GetAsync<T>(string uri, string clientId) 
    { 
     try 
     { 
     string token = await _tokenProvider.IsTokenNullOrExpired(); 
     if (!string.IsNullOrEmpty(token)) 
      { 
      using (var client = new HttpClient()) 
      { 
       ConfigurateHttpClient(client, token, clientId); 

       HttpResponseMessage response = await client.GetAsync(uri); 

       if (response.IsSuccessStatusCode) 
       { 
        return await response.Content.ReadAsAsync<T>(); 
       } 

       var exception = new Exception($"Resource server returned an error. StatusCode : {response.StatusCode}"); 
       exception.Data.Add("StatusCode", response.StatusCode); 
       throw exception; 
      } 
      } 
      else 
      { 
       var exception = new Exception(); 
       exception.Data.Add("StatusCode", HttpStatusCode.Unauthorized); 
       throw exception; 
      } 
     } 
     catch (Exception ex) 
     { 
      throw; 
     } 
    } 
1

बेहतर सवाल यह है कि पुनर्रचना से शुरू करेंगे; आपके समाधान में, आपका एमवीसी एप्लिकेशन आपके वेबपी एप्लिकेशन के साथ संचार कैसे कर रहा है? यहां सबसे अच्छा अभ्यास क्या हैं?

यहाँ का सर्वोत्तम तरीका ग्राहक के लिए है सीधे वेब एपीआई नियंत्रकों से और MVC नियंत्रकों के लिए डेटा पुनः प्राप्त करने के लिए केवल शुद्ध एचटीएमएल बार देखा गया जो लेआउट, शैलियों (सीएसएस) शामिल की सेवा (अपने मामले में ब्राउज़र) , दृश्य संरचना, स्क्रिप्ट (यानी जावास्क्रिप्ट) आदि और डेटा नहीं।

Browser communicating to MVC and Web API

छवि क्रेडिट: Ode to Code। संयोग से उस साइट पर लेखक भी आपके दृष्टिकोण की अनुशंसा नहीं करता है हालांकि इसे एक विकल्प के रूप में सूचीबद्ध किया गया है।

  1. या तो भाग में परिवर्तन करने की उतनी ही आसानी से की अनुमति देता है अपने विचारों और अपने डेटा के बीच एक अच्छा SOC के रूप में यह सर्वर।
  2. यह क्लाइंट (ब्राउज़र) को अतुल्यकालिक डेटा पुनर्प्राप्त करने की अनुमति देता है जो एक बेहतर उपयोगकर्ता अनुभव के लिए बनाता है।

यह नहीं कर रहे हैं और कॉल स्टैक में किसी नेटवर्क अनुरोध कदम जोड़ने आप डेटा का प्रवाह (वेब ​​एपीआई तैनाती को MVC नियंत्रक (रों) से कॉल) में एक अनावश्यक महंगा कदम बनाया है करके। धीमे निष्पादन को निष्पादित करने के दौरान अधिक सीमाएं पार हो जाती हैं।

जितना तेज़ समाधान, जैसा कि आपने पहले ही पता लगाया है, सीधे अपने एमवीसी प्रोजेक्ट से अपनी व्यावसायिक कोड लाइब्रेरी को कॉल करना है। यह अतिरिक्त और अनावश्यक नेटवर्क कदम से बच जाएगा। ऐसा करने में कुछ भी गलत नहीं है और कई और पारंपरिक साइटें एक ही कॉल में दृश्य (एचटीएमएल) और डेटा दोनों की सेवा करती हैं। यह एक अधिक कसकर युग्मित डिजाइन के लिए बनाता है लेकिन यह आपके पास से बेहतर है।

सबसे अच्छा दीर्घकालिक समाधान एमवीसी विचारों को बदलना है ताकि वे सीधे आपके वेब एपीआई परिनियोजन को कॉल कर सकें। यह Angular, React, , आदि जैसे ढांचे का उपयोग करके किया जा सकता है। यदि वेब एपीआई विधि कॉल सीमित हैं और बढ़ने की उम्मीद नहीं है तो आप JQuery या शुद्ध जावास्क्रिप्ट का उपयोग भी कर सकते हैं लेकिन मैं एक जटिल अनुप्रयोग बनाने की कोशिश नहीं करता यह, एक कारण है कि Angular जैसे ढांचे इतने लोकप्रिय हो गए हैं।

इस मामले में वास्तविक अंतर्निहित तकनीकी समस्या के रूप में हम यह सुनिश्चित नहीं कर सकते कि मेमोरी डंप के बिना कौन से संसाधन डेडलॉक का कारण बन रहे हैं। यह सुनिश्चित करने के रूप में के रूप में मामूली समस्या हो अपने MVC कार्रवाई के तरीके भी async Task<ActionResult> लौट रहे हैं ताकि वे एक वास्तविक async/await पद्धति का उपयोग कर HttpClient कॉल कर सकते हैं (बस ActionResult जो, मैं अनुमान लगा रहा हूँ के बजाय , आप कैसे उन्हें अब संरचित है)। ईमानदारी से, क्योंकि यह एक खराब डिजाइन है, मैं इसे काम करने के लिए कोशिश करने में किसी भी समय खर्च नहीं करता।

0

आप अपने भीतर प्रतीक्षा कर रहा है बयान करने के लिए .ConfigureAwait(false) रखना चाहिए:

HttpResponseMessage response = await client.GetAsync(uri).ConfigureAwait(false); 

(...) 

return await response.Content.ReadAsAsync<T>().ConfigureAwait(false); 

(...) 

string token = await _tokenProvider.IsTokenNullOrExpired().ConfigureAwait(false); 

(...) 
return await method(token).ConfigureAwait(false);; 

(...) 

string token = await _tokenProvider.GetTokenAsync().ConfigureAwait(false);; 

(...) 

return await method(token).ConfigureAwait(false); 

इस तरह से आप से पहले इंतजार किया जाता है तुल्यकालन संदर्भ कब्जा करने के लिए नहीं आने देगा। अन्यथा इस संदर्भ में निरंतरता की जाएगी, जिसके परिणामस्वरूप लॉक हो सकता है यदि यह अन्य धागे द्वारा उपयोग में है। ऐसा करने से कार्यवाही का इंतजार करने के लिए निरंतरता की अनुमति दी जाएगी।

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