विजुअल स्टूडियो की तुलना में पॉवर्सशेल में चलने पर HttpClient समवर्ती व्यवहार अलग


10

मैं बी 2 सी में उपयोगकर्ताओं को बनाने के लिए एमएस ग्राफ़ एपीआई का उपयोग करके एज़ एडी एडी बी 2 सी पर लाखों से अधिक उपयोगकर्ताओं को माइग्रेट कर रहा हूं। मैंने इस माइग्रेशन को करने के लिए एक .Net कोर 3.1 कंसोल एप्लिकेशन लिखा है। चीजों को गति देने के लिए मैं ग्राफ एपीआई पर समवर्ती कॉल कर रहा हूं। यह बहुत अच्छा काम कर रहा है।

विकास के दौरान मैंने विजुअल स्टूडियो 2019 से चलने के दौरान स्वीकार्य प्रदर्शन का अनुभव किया, लेकिन टेस्ट के लिए मैं पॉवरशेल 7 में कमांड लाइन से चल रहा हूं। पॉवर्सशेल से HttpClient के समवर्ती कॉल का प्रदर्शन बहुत खराब है। ऐसा प्रतीत होता है कि पॉवरशेल से चलते समय HttpClient को अनुमति देने वाले समवर्ती कॉल की संख्या की सीमा होती है, इसलिए समवर्ती बैचों में 40 से 50 से अधिक अनुरोधों को कॉल करना शुरू हो जाता है। ऐसा लगता है कि बाकी को अवरुद्ध करते हुए 40 से 50 समवर्ती अनुरोध चल रहे हैं।

मैं async प्रोग्रामिंग के साथ सहायता के लिए नहीं देख रहा हूँ। मैं विजुअल स्टूडियो रन-टाइम व्यवहार और पॉवर्सशेल कमांड लाइन रन-टाइम व्यवहार के बीच अंतर को शूट करने में परेशानी का एक तरीका ढूंढ रहा हूं। विजुअल स्टूडियो के ग्रीन एरो बटन से रिलीज़ मोड में चल रहा है जैसा कि अपेक्षित है। कमांड लाइन से नहीं चल रहा है।

मैं async कॉल के साथ एक कार्य सूची भरता हूं और फिर Task.WhenAll (कार्य) का इंतजार करता हूं। प्रत्येक कॉल में 300 से 400 मिली सेकेंड लगते हैं। जब Visual Studio से चल रहा है, यह अपेक्षित रूप से काम करता है। मैं 1000 कॉल के समवर्ती बैच बनाता हूं और प्रत्येक व्यक्तिगत रूप से अपेक्षित समय के भीतर पूरा होता है। पूरे टास्क ब्लॉक में सबसे लंबी व्यक्तिगत कॉल की तुलना में सिर्फ कुछ मिलीसेकंड अधिक समय लगता है।

जब मैं Powershell कमांड लाइन से एक ही बिल्ड चलाता हूं तो व्यवहार बदल जाता है। पहले 40 से 50 कॉल अपेक्षित 300 से 400 मिली सेकेंड लगते हैं लेकिन फिर व्यक्तिगत कॉल समय प्रत्येक 20 सेकंड तक बढ़ता है। मुझे लगता है कि कॉल धारावाहिक हो रहे हैं, इसलिए केवल 40 से 50 को एक बार में निष्पादित किया जा रहा है जबकि अन्य प्रतीक्षा कर रहे हैं।

घंटों परीक्षण और त्रुटि के बाद मैं इसे HttpClient तक सीमित करने में सक्षम था। समस्या को अलग करने के लिए मैंने HttpClient.SendAsync को कॉल करने के लिए एक विधि का उपयोग किया, जो कार्य करता है। Task.Delay (300) और एक नकली परिणाम देता है। इस स्थिति में कंसोल से चल रहा है विजुअल स्टूडियो से चलने के लिए व्यावहारिक रूप से व्यवहार करता है।

मैं IHttpClientFactory का उपयोग कर रहा हूं और मैंने ServicePointManager पर कनेक्शन सीमा को समायोजित करने की भी कोशिश की है।

यहां मेरा पंजीकरण कोड है।

    public static IServiceCollection RegisterHttpClient(this IServiceCollection services, int batchSize)
    {
        ServicePointManager.DefaultConnectionLimit = batchSize;
        ServicePointManager.MaxServicePoints = batchSize;
        ServicePointManager.SetTcpKeepAlive(true, 1000, 5000);

        services.AddHttpClient(MSGraphRequestManager.HttpClientName, c =>
        {
            c.Timeout = TimeSpan.FromSeconds(360);
            c.DefaultRequestHeaders.Add("User-Agent", "xxxxxxxxxxxx");
        })
        .ConfigurePrimaryHttpMessageHandler(() => new DefaultHttpClientHandler(batchSize));

        return services;
    }

यहाँ DefaultHttpClientHandler है।

internal class DefaultHttpClientHandler : HttpClientHandler
{
    public DefaultHttpClientHandler(int maxConnections)
    {
        this.MaxConnectionsPerServer = maxConnections;
        this.UseProxy = false;
        this.AutomaticDecompression = System.Net.DecompressionMethods.GZip | System.Net.DecompressionMethods.Deflate;
    }
}

यहां वह कोड है जो कार्यों को सेट करता है।

        var timer = Stopwatch.StartNew();
        var tasks = new Task<(UpsertUserResult, TimeSpan)>[users.Length];
        for (var i = 0; i < users.Length; ++i)
        {
            tasks[i] = this.CreateUserAsync(users[i]);
        }

        var results = await Task.WhenAll(tasks);
        timer.Stop();

यहां बताया गया है कि कैसे मैंने HttpClient का मजाक उड़ाया।

        var httpClient = this.httpClientFactory.CreateClient(HttpClientName);
        #if use_http
            using var response = await httpClient.SendAsync(request);
        #else
            await Task.Delay(300);
            var graphUser = new User { Id = "mockid" };
            using var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonConvert.SerializeObject(graphUser)) };
        #endif
        var responseContent = await response.Content.ReadAsStringAsync();

500 समवर्ती अनुरोधों का उपयोग करके ग्राफएपीआई के माध्यम से बनाए गए 10k बी 2 सी उपयोगकर्ताओं के लिए यहां मैट्रिक्स दिए गए हैं। पहले 500 अनुरोध सामान्य से अधिक हैं क्योंकि टीसीपी कनेक्शन बनाए जा रहे हैं।

यहाँ कंसोल रन मेट्रिक्स का लिंक दिया गया है

यहां विजुअल स्टूडियो रन मेट्रिक्स का लिंक दिया गया है ।

वीएस रन मेट्रिक्स में ब्लॉक का समय इस पोस्ट में मेरे द्वारा कही गई बातों से अलग है क्योंकि मैंने समस्याग्रस्त कोड को परीक्षण रन के लिए जितना संभव हो सके उतना अलग करने के प्रयास में प्रक्रिया के अंत तक सभी सिंक्रोनस फ़ाइल का उपयोग किया।

यह परियोजना .Net Core 3.1 का उपयोग करके संकलित की गई है। मैं विजुअल स्टूडियो 2019 16.4.5 का उपयोग कर रहा हूं।


2
क्या आपने पहले बैच के बाद नेटस्टैट उपयोगिता के साथ अपने कनेक्शन की स्थिति की समीक्षा की है? यह पहले कुछ कार्यों के पूरा होने के बाद क्या चल रहा है, इसके बारे में कुछ जानकारी दे सकता है।
प्रणव नेगांधी

यदि आप इसे इस तरह से हल करने का प्रयास नहीं करते हैं (HTTP अनुरोध के रूप में), तो आप हमेशा एक समवर्ती क्यू [ऑब्जेक्ट] उपभोक्ता / निर्माता समानता में प्रत्येक उपयोगकर्ता के लिए सिंक कॉल का उपयोग कर सकते हैं। मैंने हाल ही में PowerShell में लगभग 200million फ़ाइलों के लिए यह किया है।
thepip3r

1
@ thepip3r मैंने इस बार आपकी प्रशंसा को फिर से पढ़ा और समझा। में इसे याद रखूंगा।
मार्क लुटेर

1
नहीं, मैं कह रहा हूँ, अगर आप c #: leeholmes.com/blog/2018/09/05/… के बजाय PowerShell जाना चाहते थे ।
thepip3r

1
@ thepip3r बस स्टीफन क्लीयर से ब्लॉग प्रविष्टि पढ़ें। मुझे अच्छा होना चाहिए।
मार्क लॉटर

जवाबों:


3

दो बातें दिमाग में आती हैं। अधिकांश माइक्रोसॉफ़्ट पॉवरशेल संस्करण 1 और 2 में लिखे गए थे। संस्करण 1 और 2 में System.Threading.Thread.ApartmentState MTA है। 5 के माध्यम से संस्करण 3 में अपार्टमेंट राज्य डिफ़ॉल्ट रूप से एसटीए में बदल गया।

दूसरा विचार यह है कि ऐसा लगता है कि वे System.Threading.ThreadPool का उपयोग थ्रेड्स को प्रबंधित करने के लिए कर रहे हैं। आपका थ्रेडपुल कितना बड़ा है?

यदि वे समस्या हल नहीं करते हैं तो System.Threading के तहत खुदाई शुरू करें।

जब मैंने आपका प्रश्न पढ़ा तो मैंने इस ब्लॉग के बारे में सोचा। https://devblogs.microsoft.com/oldnewthing/20170623-00/?p=96455

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

अपडेट 1: मैंने स्टार्ट मेनू से पावरशेल 7.0 चलाया और थ्रेड स्टेट एसटीए था। क्या थ्रेड अवस्था दो संस्करणों में भिन्न है?

PS C:\Program Files\PowerShell\7>  [System.Threading.Thread]::CurrentThread

ManagedThreadId    : 12
IsAlive            : True
IsBackground       : False
IsThreadPoolThread : False
Priority           : Normal
ThreadState        : Running
CurrentCulture     : en-US
CurrentUICulture   : en-US
ExecutionContext   : System.Threading.ExecutionContext
Name               : Pipeline Execution Thread
ApartmentState     : STA

अपडेट 2: मैं बेहतर उत्तर की कामना करता हूं लेकिन, आप दोनों वातावरणों की तुलना तब तक करेंगे जब तक कि कुछ सामने न आ जाए।

PS C:\Windows\system32> [System.Net.ServicePointManager].GetProperties() | select name

Name                               
----                               
SecurityProtocol                   
MaxServicePoints                   
DefaultConnectionLimit             
MaxServicePointIdleTime            
UseNagleAlgorithm                  
Expect100Continue                  
EnableDnsRoundRobin                
DnsRefreshTimeout                  
CertificatePolicy                  
ServerCertificateValidationCallback
ReusePort                          
CheckCertificateRevocationList     
EncryptionPolicy            

अपडेट 3:

https://docs.microsoft.com/en-us/uwp/api/windows.web.http.httpclient

इसके अलावा, हर HttpClient इंस्टेंस अपने स्वयं के कनेक्शन पूल का उपयोग करता है, अन्य HttpClient इंस्टेंस द्वारा निष्पादित अनुरोधों से अपने अनुरोधों को अलग करता है।

अगर कोई ऐप विंडोज में HttpClient और संबंधित वर्गों का उपयोग करता है। Http नाम से बड़ी मात्रा में डेटा (50 मेगाबाइट या अधिक) डाउनलोड होते हैं, तो ऐप को उन डाउनलोड को स्ट्रीम करना चाहिए और डिफ़ॉल्ट बफरिंग का उपयोग नहीं करना चाहिए। यदि डिफ़ॉल्ट बफ़रिंग का उपयोग किया जाता है तो क्लाइंट मेमोरी का उपयोग बहुत बड़ा हो जाएगा, संभवतः कम प्रदर्शन के परिणामस्वरूप।

बस दो वातावरणों की तुलना करते रहना चाहिए और मुद्दे को बाहर रखना चाहिए

Add-Type -AssemblyName System.Net.Http
$client = New-Object -TypeName System.Net.Http.Httpclient
$client | format-list *

DefaultRequestHeaders        : {}
BaseAddress                  : 
Timeout                      : 00:01:40
MaxResponseContentBufferSize : 2147483647

जब Powershell 7.0 में चल रहा है। System.Thread.Thread.CurrentThread.GetApartmentState () प्रोग्राम MTA के भीतर से MTA देता है। ()
Mark Lauter

डिफ़ॉल्ट मिनट थ्रेड पूल 12 था, मैंने अपने बैच के आकार (परीक्षण के लिए 500) में पूल के आकार को बढ़ाने की कोशिश की। इससे व्यवहार पर कोई प्रभाव नहीं पड़ा।
मार्क लैटर

दोनों वातावरण में कितने धागे उत्पन्न होते हैं?
एरोन

मैं सोच रहा था कि 'HttpClient ’के पास कितने सूत्र हैं क्योंकि यह सभी काम कर रहा है।
एरोन

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