Asinxron operatsiyalarni optimallashtirish va ortiqcha yuklanishni oldini olish uchun Promise Pool-lari va Rate Limiting yordamida JavaScript-da ilg'or parallelizmni o'rganing.
JavaScript-da parallelizm naqshlari: Promise Pool-lari va Rate Limiting
Zamonaviy JavaScript dasturlashda asinxron operatsiyalar bilan ishlash asosiy talab hisoblanadi. API-lardan ma'lumotlarni olish, katta hajmdagi ma'lumotlar to'plamini qayta ishlash yoki foydalanuvchi bilan o'zaro aloqalarni boshqarish bo'ladimi, parallelizmni samarali boshqarish unumdorlik va barqarorlik uchun juda muhim. Bu muammoni hal qiluvchi ikkita kuchli naqsh bu Promise Pool-lari va Rate Limiting. Ushbu maqolada biz bu tushunchalarni chuqur o'rganamiz, amaliy misollar keltiramiz va ularni loyihalaringizda qanday qo'llashni ko'rsatamiz.
Asinxron operatsiyalar va parallelizmni tushunish
JavaScript o'z tabiatiga ko'ra bir oqimli (single-threaded). Bu bir vaqtning o'zida faqat bitta operatsiya bajarilishi mumkinligini anglatadi. Biroq, asinxron operatsiyalarning (callback, Promise va async/await kabi usullardan foydalangan holda) joriy etilishi JavaScript-ga asosiy oqimni bloklamasdan bir nechta vazifani parallel ravishda bajarishga imkon beradi. Parallelizm, shu nuqtai nazardan, bir vaqtning o'zida bajarilayotgan bir nechta vazifani boshqarishni anglatadi.
Quyidagi stsenariylarni ko'rib chiqing:
- Boshqaruv panelini to'ldirish uchun bir vaqtning o'zida bir nechta API-lardan ma'lumotlarni olish.
- Ko'p sonli tasvirlarni to'plam (batch) tarzida qayta ishlash.
- Ma'lumotlar bazasi bilan o'zaro aloqani talab qiladigan bir nechta foydalanuvchi so'rovlarini qayta ishlash.
To'g'ri parallelizm boshqaruvisiz siz unumdorlikning pasayishi, kechikishning ortishi va hatto dastur beqarorligiga duch kelishingiz mumkin. Masalan, API-ga juda ko'p so'rovlar yuborish tezlikni cheklash xatolariga yoki hatto xizmatning ishdan chiqishiga olib kelishi mumkin. Xuddi shunday, bir vaqtning o'zida juda ko'p CPU-ni talab qiladigan vazifalarni bajarish mijoz yoki server resurslarini haddan tashqari yuklashi mumkin.
Promise Pool-lari: Parallel vazifalarni boshqarish
Promise Pool — bu parallel asinxron operatsiyalar sonini cheklash uchun mo'ljallangan mexanizm. U bir vaqtning o'zida faqat ma'lum miqdordagi vazifalar bajarilishini ta'minlaydi, bu esa resurslarning tugashini oldini oladi va javob qaytarish tezligini saqlaydi. Ushbu naqsh, ayniqsa, parallel ravishda bajarilishi mumkin bo'lgan, ammo tezligini cheklash kerak bo'lgan ko'p sonli mustaqil vazifalar bilan ishlashda foydalidir.
Promise Pool-ni amalga oshirish
Bu yerda JavaScript-da Promise Pool-ning asosiy amalga oshirilishi keltirilgan:
class PromisePool {
constructor(concurrency) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.running < this.concurrency && this.queue.length) {
const { task, resolve, reject } = this.queue.shift();
this.running++;
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
} finally {
this.running--;
this.processQueue(); // Navbatdagi keyingi vazifani qayta ishlash
}
}
}
}
Izoh:
PromisePool
klassi bir vaqtning o'zida bajarilishi mumkin bo'lgan vazifalarning maksimal sonini belgilaydiganconcurrency
parametrini qabul qiladi.add
metodi navbatga vazifa (Promise qaytaradigan funksiya) qo'shadi. U vazifa tugagach, resolve yoki reject bo'ladigan Promise qaytaradi.processQueue
metodi bo'sh joylar (this.running < this.concurrency
) va navbatda vazifalar borligini tekshiradi. Agar shunday bo'lsa, u navbatdan vazifani oladi, uni bajaradi varunning
hisoblagichini yangilaydi.finally
bloki, vazifa muvaffaqiyatsiz bo'lsa ham,running
hisoblagichining kamaytirilishini va navbatdagi keyingi vazifani qayta ishlash uchunprocessQueue
metodining qayta chaqirilishini ta'minlaydi.
Foydalanish misoli
Aytaylik, sizda URL-lar massivi bor va siz har bir URL-dan fetch
API yordamida ma'lumotlarni olishni xohlaysiz, lekin siz serverni ortiqcha yuklamaslik uchun bir vaqtning o'zida bajariladigan so'rovlar sonini cheklamoqchisiz.
async function fetchData(url) {
console.log(`Ma'lumotlar ${url} dan olinmoqda`);
// Tarmoq kechikishini simulyatsiya qilish
await new Promise(resolve => setTimeout(resolve, Math.random() * 1000));
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP xatosi! status: ${response.status}`);
}
return await response.json();
}
async function main() {
const urls = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
'https://jsonplaceholder.typicode.com/todos/5',
'https://jsonplaceholder.typicode.com/todos/6',
'https://jsonplaceholder.typicode.com/todos/7',
'https://jsonplaceholder.typicode.com/todos/8',
'https://jsonplaceholder.typicode.com/todos/9',
'https://jsonplaceholder.typicode.com/todos/10',
];
const pool = new PromisePool(3); // Parallelizmni 3 bilan cheklash
const promises = urls.map(url => pool.add(() => fetchData(url)));
try {
const results = await Promise.all(promises);
console.log('Natijalar:', results);
} catch (error) {
console.error('Ma\'lumotlarni olishda xatolik:', error);
}
}
main();
Ushbu misolda PromisePool
3 parallelizm bilan sozlanga. urls.map
funksiyasi har bir URL-dan ma'lumotlarni olish vazifasini ifodalovchi Promise-lar massivini yaratadi. pool.add
metodi har bir vazifani Promise Pool-ga qo'shadi, bu esa ushbu vazifalarning parallel bajarilishini boshqaradi va bir vaqtning o'zida 3 tadan ortiq so'rov bajarilmasligini ta'minlaydi. Promise.all
funksiyasi barcha vazifalar bajarilishini kutadi va natijalar massivini qaytaradi.
Rate Limiting: API-dan suiiste'mol qilish va xizmatni ortiqcha yuklashning oldini olish
Rate limiting - bu mijozlar (yoki foydalanuvchilar) xizmat yoki API-ga qanday tezlikda so'rovlar yuborishini nazorat qilish usulidir. Bu suiiste'molchilikning oldini olish, xizmat ko'rsatishni rad etish (DoS) hujumlaridan himoyalanish va resurslardan adolatli foydalanishni ta'minlash uchun zarur. Rate limiting mijoz tomonida, server tomonida yoki ikkalasida ham amalga oshirilishi mumkin.
Nima uchun Rate Limiting-dan foydalanish kerak?
- Suiiste'molning oldini olish: Bitta foydalanuvchi yoki mijozning ma'lum bir vaqt oralig'ida yuborishi mumkin bo'lgan so'rovlar sonini cheklaydi, bu ularning serverni haddan tashqari ko'p so'rovlar bilan yuklashiga yo'l qo'ymaydi.
- DoS hujumlaridan himoya: Hujumchilarning so'rovlar yuborish tezligini cheklash orqali tarqatilgan xizmat ko'rsatishni rad etish (DDoS) hujumlarining ta'sirini kamaytirishga yordam beradi.
- Adolatli foydalanishni ta'minlash: So'rovlarni teng taqsimlash orqali turli foydalanuvchilar yoki mijozlarga resurslardan adolatli foydalanish imkonini beradi.
- Unumdorlikni oshirish: Serverning haddan tashqari yuklanishini oldini oladi, bu uning so'rovlarga o'z vaqtida javob berishini ta'minlaydi.
- Xarajatlarni optimallashtirish: API foydalanish kvotalaridan oshib ketish va uchinchi tomon xizmatlaridan qo'shimcha xarajatlarga duch kelish xavfini kamaytiradi.
JavaScript-da Rate Limiting-ni amalga oshirish
JavaScript-da rate limiting-ni amalga oshirishning turli xil yondashuvlari mavjud bo'lib, ularning har birining o'ziga xos afzalliklari va kamchiliklari bor. Bu yerda biz oddiy token chelak (token bucket) algoritmidan foydalangan holda mijoz tomonida amalga oshirishni ko'rib chiqamiz.
class RateLimiter {
constructor(capacity, refillRate, interval) {
this.capacity = capacity; // Tokenlarning maksimal soni
this.tokens = capacity;
this.refillRate = refillRate; // Har bir intervalda qo'shiladigan tokenlar
this.interval = interval; // Millisaniyali interval
setInterval(() => {
this.refill();
}, this.interval);
}
refill() {
this.tokens = Math.min(this.capacity, this.tokens + this.refillRate);
}
async consume(cost = 1) {
if (this.tokens >= cost) {
this.tokens -= cost;
return Promise.resolve();
} else {
return new Promise((resolve, reject) => {
const waitTime = Math.ceil((cost - this.tokens) / this.refillRate) * this.interval;
setTimeout(() => {
if (this.tokens >= cost) {
this.tokens -= cost;
resolve();
} else {
reject(new Error('Tezlik chegarasi oshib ketdi.'));
}
}, waitTime);
});
}
}
}
Izoh:
RateLimiter
klassi uchta parametrni qabul qiladi:capacity
(tokenlarning maksimal soni),refillRate
(har bir intervalda qo'shiladigan tokenlar soni) vainterval
(millisaniyali vaqt oralig'i).refill
metodiinterval
bo'yicharefillRate
tezligida chelakka maksimal sig'imgacha tokenlar qo'shadi.consume
metodi belgilangan miqdordagi tokenlarni (standart qiymati 1) ishlatishga harakat qiladi. Agar yetarli tokenlar mavjud bo'lsa, u ularni ishlatadi va darhol resolve qiladi. Aks holda, u yetarli tokenlar paydo bo'lguncha kutish kerak bo'lgan vaqtni hisoblaydi, o'sha vaqtni kutadi va keyin tokenlarni yana ishlatishga harakat qiladi. Agar hali ham tokenlar yetarli bo'lmasa, u xatolik bilan reject qiladi.
Foydalanish misoli
async function makeApiRequest() {
// API so'rovini simulyatsiya qilish
await new Promise(resolve => setTimeout(resolve, Math.random() * 500));
console.log('API so\'rovi muvaffaqiyatli');
}
async function main() {
const rateLimiter = new RateLimiter(5, 1, 1000); // Sekundiga 5 ta so'rov
for (let i = 0; i < 10; i++) {
try {
await rateLimiter.consume();
await makeApiRequest();
} catch (error) {
console.error('Tezlik chegarasi oshib ketdi:', error.message);
}
}
}
main();
Ushbu misolda RateLimiter
sekundiga 5 ta so'rovga ruxsat berish uchun sozlangan. main
funksiyasi 10 ta API so'rovini amalga oshiradi, ularning har biri rateLimiter.consume()
chaqiruvidan oldin keladi. Agar tezlik chegarasi oshib ketsa, consume
metodi xatolik bilan reject qiladi, bu xatolik try...catch
bloki tomonidan ushlanadi.
Promise Pool-lari va Rate Limiting-ni birlashtirish
Ba'zi stsenariylarda siz parallelizm va so'rovlar tezligini yanada nozikroq nazorat qilish uchun Promise Pool-lari va Rate Limiting-ni birlashtirishni xohlashingiz mumkin. Masalan, siz ma'lum bir API nuqtasiga bir vaqtning o'zida yuboriladigan so'rovlar sonini cheklash bilan birga, umumiy so'rovlar tezligi ma'lum bir chegaradan oshmasligini ta'minlashni xohlashingiz mumkin.
Bu ikki naqshni qanday birlashtirishingiz mumkinligi quyidagicha:
async function fetchDataWithRateLimit(url, rateLimiter) {
try {
await rateLimiter.consume();
return await fetchData(url);
} catch (error) {
throw error;
}
}
async function main() {
const urls = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
'https://jsonplaceholder.typicode.com/todos/5',
'https://jsonplaceholder.typicode.com/todos/6',
'https://jsonplaceholder.typicode.com/todos/7',
'https://jsonplaceholder.typicode.com/todos/8',
'https://jsonplaceholder.typicode.com/todos/9',
'https://jsonplaceholder.typicode.com/todos/10',
];
const pool = new PromisePool(3); // Parallelizmni 3 bilan cheklash
const rateLimiter = new RateLimiter(5, 1, 1000); // Sekundiga 5 ta so'rov
const promises = urls.map(url => pool.add(() => fetchDataWithRateLimit(url, rateLimiter)));
try {
const results = await Promise.all(promises);
console.log('Natijalar:', results);
} catch (error) {
console.error('Ma\'lumotlarni olishda xatolik:', error);
}
}
main();
Ushbu misolda fetchDataWithRateLimit
funksiyasi URL-dan ma'lumotlarni olishdan oldin RateLimiter
-dan token oladi. Bu PromisePool
tomonidan boshqariladigan parallelizm darajasidan qat'i nazar, so'rovlar tezligining cheklanishini ta'minlaydi.
Global dasturlar uchun e'tiborga olinadigan jihatlar
Global dasturlarda Promise Pool-lari va Rate Limiting-ni amalga oshirishda quyidagi omillarni hisobga olish muhim:
- Vaqt zonalari: Tezlikni cheklashni amalga oshirishda vaqt zonalariga e'tibor bering. Tezlikni cheklash mantig'ingiz izchil vaqt zonasiga asoslanganligiga yoki vaqt zonasidan mustaqil yondashuvdan (masalan, UTC) foydalanishiga ishonch hosil qiling.
- Geografik taqsimot: Agar dasturingiz bir nechta geografik mintaqalarda joylashtirilgan bo'lsa, tarmoq kechikishidagi va foydalanuvchi xulq-atvoridagi farqlarni hisobga olish uchun har bir mintaqa uchun alohida tezlikni cheklashni amalga oshirishni ko'rib chiqing. Kontent yetkazib berish tarmoqlari (CDN) ko'pincha chekka serverlarda sozlanishi mumkin bo'lgan tezlikni cheklash xususiyatlarini taklif qiladi.
- API Provayderlarining tezlik chegaralari: Dasturingiz foydalanadigan uchinchi tomon API-lari tomonidan qo'yilgan tezlik chegaralaridan xabardor bo'ling. Ushbu chegaralar doirasida qolish va bloklanishdan saqlanish uchun o'z tezlikni cheklash mantig'ingizni qo'llang. Tezlikni cheklash xatolarini to'g'ri hal qilish uchun jitter bilan eksponensial kutishdan (exponential backoff) foydalanishni ko'rib chiqing.
- Foydalanuvchi tajribasi: Foydalanuvchilar tezlik chegarasiga duch kelganda, ularga cheklov sababini va kelajakda undan qanday qochish mumkinligini tushuntiruvchi ma'lumotli xato xabarlarini taqdim eting. Turli foydalanuvchi ehtiyojlarini qondirish uchun o'zgaruvchan tezlik chegaralariga ega bo'lgan turli xil xizmat darajalarini taklif qilishni ko'rib chiqing.
- Monitoring va jurnal yozish: Potentsial to'siqlarni aniqlash va tezlikni cheklash mantig'ingiz samarali ekanligiga ishonch hosil qilish uchun dasturingizning parallelizmi va so'rovlar tezligini kuzatib boring. Foydalanish naqshlarini kuzatish va potentsial suiiste'molliklarni aniqlash uchun tegishli metrikalarni jurnalga yozib boring.
Xulosa
Promise Pool-lari va Rate Limiting JavaScript dasturlarida parallelizmni boshqarish va ortiqcha yuklanishni oldini olish uchun kuchli vositalardir. Ushbu naqshlarni tushunib, ularni samarali qo'llash orqali siz dasturlaringizning unumdorligini, barqarorligini va masshtablanuvchanligini oshirishingiz mumkin. Oddiy veb-ilova yoki murakkab taqsimlangan tizim quryapsizmi, bu tushunchalarni o'zlashtirish mustahkam va ishonchli dasturiy ta'minot yaratish uchun zarurdir.
Dasturingizning o'ziga xos talablarini diqqat bilan ko'rib chiqishni va mos parallelizm boshqaruv strategiyasini tanlashni unutmang. Unumdorlik va resurslardan foydalanish o'rtasidagi optimal muvozanatni topish uchun turli xil konfiguratsiyalar bilan tajriba o'tkazing. Promise Pool-lari va Rate Limiting-ni puxta tushungan holda, siz zamonaviy JavaScript dasturlashining qiyinchiliklariga yaxshi tayyor bo'lasiz.