আমি সম্প্রতি এইচটিটিপি কল থ্রুপুট পরীক্ষার জন্য একটি সাধারণ অ্যাপ্লিকেশন তৈরি করেছি যা একটি ক্লাসিকাল মাল্টিথ্রেডেড পদ্ধতির বনাম একটি অ্যাসিনক্রোনাস পদ্ধতিতে তৈরি করা যেতে পারে।
অ্যাপ্লিকেশনটি এইচটিটিপি কলগুলির একটি পূর্বনির্ধারিত সংখ্যা সম্পাদন করতে সক্ষম এবং শেষে এটি সম্পাদন করার জন্য প্রয়োজনীয় সময়টি প্রদর্শন করে। আমার পরীক্ষার সময়, সমস্ত HTTP কলগুলি আমার স্থানীয় আইআইএস সেভারে করা হয়েছিল এবং তারা একটি ছোট পাঠ্য ফাইল (আকারে 12 বাইট) উদ্ধার করেছে rie
অ্যাসিক্রোনাস বাস্তবায়নের জন্য কোডের সবচেয়ে গুরুত্বপূর্ণ অংশটি নীচে তালিকাভুক্ত করা হয়েছে:
public async void TestAsync()
{
this.TestInit();
HttpClient httpClient = new HttpClient();
for (int i = 0; i < NUMBER_OF_REQUESTS; i++)
{
ProcessUrlAsync(httpClient);
}
}
private async void ProcessUrlAsync(HttpClient httpClient)
{
HttpResponseMessage httpResponse = null;
try
{
Task<HttpResponseMessage> getTask = httpClient.GetAsync(URL);
httpResponse = await getTask;
Interlocked.Increment(ref _successfulCalls);
}
catch (Exception ex)
{
Interlocked.Increment(ref _failedCalls);
}
finally
{
if(httpResponse != null) httpResponse.Dispose();
}
lock (_syncLock)
{
_itemsLeft--;
if (_itemsLeft == 0)
{
_utcEndTime = DateTime.UtcNow;
this.DisplayTestResults();
}
}
}
মাল্টিথ্রেডিং বাস্তবায়নের সর্বাধিক গুরুত্বপূর্ণ অংশটি নীচে তালিকাভুক্ত করা হয়েছে:
public void TestParallel2()
{
this.TestInit();
ServicePointManager.DefaultConnectionLimit = 100;
for (int i = 0; i < NUMBER_OF_REQUESTS; i++)
{
Task.Run(() =>
{
try
{
this.PerformWebRequestGet();
Interlocked.Increment(ref _successfulCalls);
}
catch (Exception ex)
{
Interlocked.Increment(ref _failedCalls);
}
lock (_syncLock)
{
_itemsLeft--;
if (_itemsLeft == 0)
{
_utcEndTime = DateTime.UtcNow;
this.DisplayTestResults();
}
}
});
}
}
private void PerformWebRequestGet()
{
HttpWebRequest request = null;
HttpWebResponse response = null;
try
{
request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "GET";
request.KeepAlive = true;
response = (HttpWebResponse)request.GetResponse();
}
finally
{
if (response != null) response.Close();
}
}
পরীক্ষা চালানো থেকে জানা গেল যে মাল্টিথ্রেডেড সংস্করণটি দ্রুত ছিল। এটি 10 কে অনুরোধগুলি সম্পূর্ণ করতে প্রায় 0.6 সেকেন্ড সময় নিয়েছে, যখন অ্যাসিঙ্ক এক একই পরিমাণের বোঝা শেষ করতে প্রায় 2 সেকেন্ড সময় নিয়েছিল। এটি কিছুটা অবাক হয়েছিল, কারণ আমি আশা করছিলাম যে অ্যাসিঙ্কটি দ্রুততর হবে। হতে পারে এটি আমার এইচটিটিপি কলগুলি খুব দ্রুত ছিল of সত্যিকারের বিশ্বের দৃশ্যে, যেখানে সার্ভারটির আরও অর্থবহ অপারেশন করা উচিত এবং যেখানে নেটওয়ার্কের কিছুটা বিলম্ব হওয়া উচিত সেখানে ফলাফলগুলি বিপরীত হতে পারে।
যাইহোক, লোড বাড়ানোর সাথে সাথে HTTPClient যেভাবে আচরণ করে তা হ'ল সত্যই আমাকে উদ্বেগিত করে। যেহেতু এটি 10 কে বার্তা সরবরাহ করতে প্রায় 2 সেকেন্ড সময় নেয়, তাই আমি ভেবেছিলাম যে 10 বারের সংখ্যার বার বার 10 সেকেন্ড দেওয়ার জন্য এটি প্রায় 20 সেকেন্ডের মতো লাগবে, তবে পরীক্ষাটি চালিয়ে গেছে যে 100 কে বার্তা সরবরাহ করতে প্রায় 50 সেকেন্ডের প্রয়োজন needs তদতিরিক্ত, 200k বার্তা সরবরাহ করতে সাধারণত 2 মিনিটের বেশি সময় লাগে এবং প্রায়শই, কয়েক হাজার (3-4-) নিম্নলিখিত ব্যতিক্রম ব্যর্থ হয়:
সকেটে একটি অপারেশন করা যায়নি কারণ সিস্টেমের পর্যাপ্ত বাফার জায়গার অভাব ছিল বা একটি সারি পূর্ণ ছিল।
আমি আইআইএস লগগুলি এবং অপারেশনগুলি যাচাই করেছিলাম যা কখনও সার্ভারে পেল না। তারা ক্লায়েন্টের মধ্যে ব্যর্থ হয়েছে। 49152 থেকে 65535 অবধি সাময়িক বন্দরগুলির ডিফল্ট পরিসীমা সহ আমি একটি উইন্ডোজ 7 মেশিনে পরীক্ষা চালিয়েছি ne বন্দরগুলির অভাব যদি সত্যই ব্যতিক্রমগুলির কারণ হয়ে দাঁড়ায় তবে এর অর্থ হ'ল নেটস্ট্যাট পরিস্থিতিটি সঠিকভাবে জানায় নি বা এইচটিসি ক্লায়েন্ট কেবলমাত্র সর্বাধিক সংখ্যক বন্দর ব্যবহার করে যার পরে এটি ব্যতিক্রম ছোঁড়া শুরু করে।
বিপরীতে, এইচটিটিপি কল উত্পন্ন করার মাল্টিথ্রিড পদ্ধতিটি খুব অনুমানযোগ্য আচরণ করেছিল। আমি এটি 10k বার্তাগুলির জন্য 0.6 সেকেন্ডের কাছাকাছি নিয়েছিলাম, 100k বার্তাগুলির জন্য প্রায় 5.5 সেকেন্ড এবং 1 মিলিয়ন বার্তাগুলির জন্য প্রায় 55 সেকেন্ডের প্রত্যাশিত। কোনও বার্তা ব্যর্থ হয়েছে। আরও, এটি চালানোর সময় এটি 55 এমবি র্যামের বেশি কখনও ব্যবহার করেনি (উইন্ডোজ টাস্ক ম্যানেজারের মতে)। অসম্পূর্ণভাবে বার্তা প্রেরণের সময় ব্যবহৃত মেমরিটি লোডের সাথে আনুপাতিকভাবে বেড়েছে grew এটি 200k বার্তা পরীক্ষার সময় প্রায় 500 এমবি র্যাম ব্যবহার করে।
আমি মনে করি উপরোক্ত ফলাফলগুলির জন্য দুটি মূল কারণ রয়েছে। প্রথমটি হ'ল এইচটিপিপ্লিয়েন্ট সার্ভারের সাথে নতুন সংযোগ তৈরি করতে খুব লোভী বলে মনে হচ্ছে। নেটস্ট্যাট দ্বারা ব্যবহৃত বন্দরগুলির উচ্চ সংখ্যার অর্থ হ'ল এটি সম্ভবত HTTP- র বেঁচে থাকার থেকে খুব বেশি উপকার করে না।
দ্বিতীয়টি হ'ল এইচটিপিপ্লেইন্টের কাছে থ্রোলটিং মেকানিজম নেই বলে মনে হচ্ছে। আসলে এটি async অপারেশন সম্পর্কিত একটি সাধারণ সমস্যা বলে মনে হচ্ছে। আপনার যদি খুব বড় সংখ্যক ক্রিয়াকলাপ সম্পাদন করা দরকার হয় তবে সেগুলি একবারে শুরু করা হবে এবং তারপরে এটি উপলব্ধ থাকায় তাদের ধারাবাহিকতা কার্যকর করা হবে। তত্ত্বের ক্ষেত্রে এটি ঠিক হওয়া উচিত, কারণ অ্যাসিঙ্ক অপারেশনগুলিতে বোঝা বাহ্যিক সিস্টেমে থাকে তবে উপরে প্রমাণিত হিসাবে এটি সম্পূর্ণরূপে নয়। প্রচুর অনুরোধ একবারে শুরু করা মেমরির ব্যবহার বাড়িয়ে তুলবে এবং পুরো সম্পাদনাকে ধীর করবে।
আমি একটি সহজ তবে আদিম বিলম্ব প্রক্রিয়া সহ অ্যাসিঙ্ক্রোনাস অনুরোধের সর্বাধিক সংখ্যাকে সীমাবদ্ধ করে আরও ভাল ফলাফল, মেমরি এবং কার্যকর করার সময় অনুযায়ী প্রাপ্ত করতে সক্ষম হয়েছি:
public async void TestAsyncWithDelay()
{
this.TestInit();
HttpClient httpClient = new HttpClient();
for (int i = 0; i < NUMBER_OF_REQUESTS; i++)
{
if (_activeRequestsCount >= MAX_CONCURENT_REQUESTS)
await Task.Delay(DELAY_TIME);
ProcessUrlAsyncWithReqCount(httpClient);
}
}
এটি সত্যিই কার্যকর হবে যদি এইচটিটিপি ক্লায়েন্ট সামনের অনুরোধগুলির সংখ্যা সীমিত করার জন্য একটি প্রক্রিয়া অন্তর্ভুক্ত করে। টাস্ক ক্লাসটি ব্যবহার করার সময় (যা নেট থ্রেড পুলের উপর ভিত্তি করে) সামঞ্জস্যিক থ্রেডের সংখ্যা সীমিত করে স্বয়ংক্রিয়ভাবে থ্রোটলিং অর্জন করা হয়।
সম্পূর্ণ পর্যালোচনার জন্য, আমি এইচটিপিপিবলেন্টের পরিবর্তে এইচটিপিওয়েব্রেকয়েস্টের ভিত্তিতে এসিএনসি পরীক্ষার একটি সংস্করণও তৈরি করেছি এবং আরও ভাল ফলাফল অর্জন করতে পেরেছি। একটি সূচনার জন্য, এটি একযোগে সংযোগগুলির সংখ্যার (সার্ভিসপয়েন্ট ম্যানেজার.ডাফল্ট সংযোগ লিমিট বা কনফিগার মাধ্যমে) সীমা নির্ধারণের অনুমতি দেয়, যার অর্থ এটি পোর্টের বাইরে চলে না এবং কোনও অনুরোধে কখনই ব্যর্থ হয় না (এইচটিটিপিপ্লিনেন্ট, ডিফল্টরূপে, HTTPWebRequest এর উপর ভিত্তি করে , তবে এটি সংযোগ সীমাবদ্ধতা সেটিংসকে উপেক্ষা করে বলে মনে হচ্ছে)।
অ্যাসিঙ্ক এইচটিটিপিওব্রেইকয়েস্ট পদ্ধতিটি এখনও বহু-পাঠ্যক্রমের চেয়ে প্রায় 50 - 60% ধীর ছিল, তবে এটি অনুমানযোগ্য এবং নির্ভরযোগ্য ছিল। এর একমাত্র অবক্ষয়টি হ'ল এটি বিশাল বোঝার নীচে বিশাল পরিমাণ স্মৃতি ব্যবহার করেছিল। উদাহরণস্বরূপ, 1 মিলিয়ন অনুরোধ প্রেরণের জন্য এটি প্রায় 1.6 গিগাবাইটের প্রয়োজন। সাম্প্রতিক অনুরোধের সংখ্যা সীমিত করে (যেমনটি আমি এইচটিটিপিপ্লিনেন্টের জন্য উপরে করেছি) আমি ব্যবহৃত মেমরিটি মাত্র 20 এমবি করে কমিয়ে আনতে পেরেছি এবং মাল্টিথ্রেডিং পদ্ধতির চেয়ে 10% ধীর গতিতে কার্যকর করার সময়টি পেয়েছি।
এই দীর্ঘ উপস্থাপনের পরে, আমার প্রশ্নগুলি: নিখরচায় লোড অ্যাপ্লিকেশনগুলির জন্য নেট .৫.৫ থেকে এইচটিপিপ্লিয়েন্ট শ্রেণি কি খারাপ পছন্দ? এটিকে থ্রটল করার কোনও উপায় আছে, যা আমি উল্লেখ করি এমন সমস্যাগুলি সমাধান করা উচিত? HttpWebRequest এর async গন্ধ সম্পর্কে কী?
আপডেট (ধন্যবাদ @ স্টিফেন ক্লিয়ারি)
যেমনটি দেখা যাচ্ছে, এইচটিপিপ্লিয়েন্ট, ঠিক এইচটিটিপিওয়েব্রেইকেষ্টের (যেমন এটি ডিফল্ট ভিত্তিক) এটির সাথে একই পয়েন্টে সার্ভিসপয়েন্ট ম্যানেজার.ডেফল্টকনেকশন লিমিটের সাথে সীমাবদ্ধ একই হোস্টে সংযোগের সংখ্যা থাকতে পারে। আশ্চর্যের বিষয়টি হ'ল এমএসডিএন অনুসারে , সংযোগের সীমাটির জন্য ডিফল্ট মান 2 I আমি এটিও পরীক্ষা করেছিলাম যে আমার দিকে ডিবাগারটি ব্যবহার করে যা নির্দেশ করে যে সত্যই 2 ডিফল্ট মান। যাইহোক, দেখে মনে হচ্ছে সার্ভিসপয়েন্ট ম্যানেজার.ডাফল্ট সংযোগ-লিমিটের স্পষ্টভাবে কোনও মান সেট না করা হলে ডিফল্ট মানটিকে অগ্রাহ্য করা হবে। যেহেতু আমার এইচটিটিপিপ্লিনেন্ট পরীক্ষার সময় আমি স্পষ্টভাবে এর জন্য কোনও মান সেট করি নি বলে আমি ভেবেছিলাম এটি উপেক্ষা করা হয়েছে।
সার্ভিসপয়েন্ট ম্যানেজার.ডাফল্টক্লিকেশনলিমিটটি 100 এইচটিপিপিপ্লাইনেট স্থাপনের পরে নির্ভরযোগ্য এবং পূর্বাভাসযোগ্য হয়ে উঠেছে (নেটস্ট্যাট নিশ্চিত করে যে কেবল 100 টি পোর্ট ব্যবহৃত হয়)। এটি এখনও async HttpWebRequest (প্রায় 40% দ্বারা) এর চেয়ে ধীরে ধীরে, তবে আশ্চর্যের বিষয় হল এটি কম স্মৃতি ব্যবহার করে। 1 মিলিয়ন অনুরোধগুলির সাথে জড়িত পরীক্ষার জন্য, এটি অ্যাসিঙ্ক এইচটিটিপিওয়েব্রেকয়েস্টের 1.6 গিগাবাইটের তুলনায় সর্বাধিক 550 এমবি ব্যবহার করে।
সুতরাং, যদিও সার্ভিসপয়েন্ট ম্যানেজার.ডাফল্টক্লিকেশন লিমিটের সংমিশ্রণে এইচটিপিপ্লিয়েন্টটি নির্ভরযোগ্যতা নিশ্চিত করে বলে মনে হচ্ছে (কমপক্ষে সেই দৃশ্যের জন্য যেখানে সমস্ত কল একই হোস্টের দিকে করা হচ্ছে), এটি এখনও মনে হচ্ছে সঠিক থ্রোটলিং ব্যবস্থা না থাকার কারণে তার অভিনয়টি নেতিবাচকভাবে প্রভাবিত হয়েছে। এমন কিছু যা অনুরোধের একযোগে সংখ্যাকে একটি কনফিগারযোগ্য মানের মধ্যে সীমাবদ্ধ রাখে এবং বাকীগুলিকে একটি কাতারে রাখে তা উচ্চ স্কেলিবিলিটি দৃশ্যের জন্য এটি আরও বেশি উপযুক্ত করে তুলবে।
SemaphoreSlim
ইতিমধ্যে উল্লিখিত হিসাবে, বা ActionBlock<T>
টিপিএল ডেটাফ্লো থেকে ব্যবহার করতে পারেন ।
HttpClient
শ্রদ্ধা করা উচিতServicePointManager.DefaultConnectionLimit
।