Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. Uchta timeout
- 2.1 Sukut qiymatlari
- 2.2 Ulanish hayoti
- 3. headersTimeout va Slowloris
- 3.1 Hujum
- 3.2 O'z serverimizda
- 3.3 Tekshiruv oralig'i: 10 s emas, 13.4 s
- 3.4 requestTimeout — sekin tana
- 4. keepAliveTimeout va sirli 502
- 4.1 Keep-alive eslatmasi
- 4.2 Node 24 da +1 soniya
- 4.3 502 qanday tug'iladi
- 5. Serverni to'g'ri to'xtatish
- 5.1 server.close() nima qiladi
- 5.2 yop() funksiyasi
- 5.3 Signallar
- 6. Hujumchi nigohi
- 7. API qadami: timeout'lar va graceful shutdown (holat 07, commit)
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): sozlama tekshiruvchisi
- 3-mashq (qiyin): majburiy uzish
- 4-mashq: API qadami
- 10. Real ishda
- Xulosa
- Manbalar
Node.js server timeout'lari: keepAliveTimeout, headersTimeout, Slowloris va graceful shutdown
Qisqacha: Node HTTP serverida uchta asosiy timeout bor.
keepAliveTimeout(sukut 5 s) — javobdan keyin bo'sh ulanish qancha ochiq turadi. U load balancer'ning bo'sh ulanish vaqtidan (~60 s) katta bo'lmasa, sirli 502 xatolar chiqadi.headersTimeout(sukut 60 s) — sarlavhalar shu vaqtda kelmasa 408 javobi. Bu Slowloris hujumiga qarshi himoya.requestTimeout(sukut 300 s) — butun so'rov uchun chegara. Serverni to'xtatishdaserver.close()yolg'iz yetmaydi. Biz o'lchadik: bo'sh keep-alive ulanish tufayliclose()7.4 s kutdi,closeIdleConnections()bilan esa 1.4 s.
Bu darsda
- Uchta timeout'ni farqlaysiz va har biri qaysi muammoni hal qilishini bilasiz.
- Load balancer ortidagi "sirli 502" ning sababini tushuntirasiz.
- Slowloris hujumini o'z serveringizda ko'rasiz va to'xtatasiz.
- Node 24 dagi
keepAliveTimeoutBufferni o'lchov bilan ko'rasiz. vazifalar-apiga timeout'lar va graceful shutdown qo'shib, holat 07 ni commit qilasiz.
Oldin bilishingiz kerak: HTTP server tomonda: keep-alive va chunked, http.createServer, process obyekti va signallar, timers chuqur.
1. Nega bu kerak?
Tasavvur qiling: vazifalar-api serverda ishlayapti, oldida load balancer (LB) bor. Kuniga bir necha marta foydalanuvchi 502 Bad Gateway oladi. Loglarda xato yo'q. Qayta urinsa — ishlaydi. Bu xato oylab "tasodif" deb yuradi. Aslida sababi bitta raqamda: Node bo'sh ulanishni LB'dan oldin yopadi.
Ikkinchi holat: kimdir serveringizga yuzlab ulanish ochadi va har biriga juda sekin, bittadan sarlavha qatori yuboradi. Server har birini kutadi va yangi foydalanuvchilarga joy qolmaydi. Bu — Slowloris hujumi.
Uchinchisi: yangi versiyani chiqaryapsiz va eski jarayon to'xtaydi. Ayni paytda kimdir vazifa saqlayotgan edi — so'rovi uzilib ketdi.
Uchala holat ham — timeout (kutish chegarasi) masalasi. O'xshatish: oshxona ofitsianti. Mehmon menyuni 10 daqiqa o'qisa — kutadi. Bir soat o'qisa — boshqa mehmonga o'tadi (headersTimeout). Mehmon ovqatdan keyin stolda o'tiraversa — choy yana kerakmi deb so'raydi, keyin stolni bo'shatadi (keepAliveTimeout). Oshxona yopilayotganda yangi mehmon kirmaydi, lekin o'tirganlar ovqatini tugatadi (graceful shutdown).
2. Uchta timeout
2.1 Sukut qiymatlari
Kanon tajribasi Node 24.21.0 da sukut qiymatlarni chiqardi:
| Sozlama | Sukut | Nimani kutadi |
|---|---|---|
keepAliveTimeout |
5 000 ms | javobdan keyin bo'sh ulanishda keyingi so'rovni |
headersTimeout |
60 000 ms | so'rovning sarlavhalari to'liq kelishini |
requestTimeout |
300 000 ms | butun so'rov (sarlavha + tana) kelishini |
connectionsCheckingInterval |
30 000 ms | Node muddati o'tgan ulanishlarni qanchada tekshiradi |
Hammasi createServer ning birinchi argumentida beriladi:
const server = createServer({
keepAliveTimeout: 65_000,
headersTimeout: 10_000,
requestTimeout: 30_000,
}, ishlovchi);E'tibor bering: headersTimeout va requestTimeout — mijoz yuborishi uchun vaqt. Sizning ishlovchingiz qancha ishlashini ular cheklamaydi. Sekin bazaga so'rov uchun alohida timeout kerak (masalan AbortSignal.timeout, timers darsi).
Oxirgi qatorga ham qarang: Node har ulanishni har millisekundda tekshirmaydi. U 30 soniyada bir marta "muddati o'tganlar"ni qidiradi. Demak headersTimeout: 10_000 amalda 10–40 s bo'lishi mumkin. Buni pastda o'lchaymiz.
2.2 Ulanish hayoti
sequenceDiagram
participant M as Mijoz
participant S as Node server
M->>S: TCP ulanish
Note over S: headersTimeout boshlandi
M->>S: GET /vazifalar + sarlavhalar
Note over S: requestTimeout (tana bilan)
S-->>M: 200 OK, Keep-Alive: timeout=5
Note over S: keepAliveTimeout boshlandi
M->>S: keyingi so'rov (shu ulanishda)
S-->>M: 200 OK
Note over S: 5 s + 1 s bo'sh — ulanish yopiladiDiagrammadagi har Note — bitta taymer. Birinchi ikkitasi mijoz yuborayotganda ishlaydi, oxirgisi — so'rovlar orasidagi bo'sh vaqtda.
3. headersTimeout va Slowloris
3.1 Hujum
Slowloris — 2009 yilda tasvirlangan hujum. Hujumchi ulanish ochadi va sarlavhalarni juda sekin, bittadan yuboradi, lekin so'rovni hech qachon tugatmaydi (sarlavhalar oxiridagi bo'sh qator kelmaydi). Server "hali kelyapti" deb kutadi. Bunday ulanishlar yuzta bo'lsa — server yangi mijozlarni qabul qila olmaydi. Hujumchiga katta internet ham, ko'p kompyuter ham kerak emas.
3.2 O'z serverimizda
Quyidagi blok kichik server ochadi (headersTimeout: 2000) va unga o'zi Slowloris bilan hujum qiladi: har 400 ms da bitta sarlavha qatori. node:net — xom TCP soket (net darsi), chunki fetch sarlavhalarni bittalab yubora olmaydi:
import { createServer } from "node:http";
import { connect } from "node:net";
const server = createServer({
headersTimeout: 2000, // sarlavhalar 2 s ichida kelsin
connectionsCheckingInterval: 500, // har 0.5 s tekshirish
}, (req, res) => res.end("ok"));
await new Promise((ok) => server.listen(0, "127.0.0.1", ok));
const t0 = performance.now();
const soket = connect(server.address().port, "127.0.0.1");
soket.write("GET / HTTP/1.1\r\nHost: x\r\n");
// Slowloris: har 400 ms da bitta "sarlavha", oxiri hech kelmaydi
const iv = setInterval(() => soket.write("X-Sekin: 1\r\n"), 400);
let javob = "";
soket.on("data", (b) => {
javob += b;
clearInterval(iv); // javob keldi — yozishni to'xtatamiz
});
soket.on("error", () => {}); // yopilishdagi ECONNRESET
await new Promise((ok) => soket.on("close", ok));
clearInterval(iv);
const s = Math.round((performance.now() - t0) / 1000);
console.log(`~${s} s dan keyin yopildi:`, javob.split("\r\n")[0]);
server.closeAllConnections();
server.close();Konsolda:
~2 s dan keyin yopildi: HTTP/1.1 408 Request Timeout408 Request Timeout — "so'rovni kutish vaqti tugadi". Server javob yozdi va ulanishni yopdi. Kanondagi aniq o'lchov — 2 055 ms. Xuddi shu hujum sukut sozlamalarda (60 s, tekshiruv har 30 s) 5 soniya ichida umuman yopilmadi.
3.3 Tekshiruv oralig'i: 10 s emas, 13.4 s
Kanondagi vazifalar-api (holat 07) headersTimeout: 10_000 bilan, lekin connectionsCheckingInterval sukut (30 s) bilan ishga tushdi. Jonli Slowloris (har 1 s da sarlavha):
Slowloris: server 13373 ms da yopdi, javob: "HTTP/1.1 408 Request Timeout"10 s emas, 13.4 s. Node muddati o'tgan ulanishni keyingi tekshiruvda topdi. Eng yomon holatda bu 10 + 30 = 40 s bo'lishi mumkin. Kanon tekshiruv oralig'ini o'zgartirmadi: 13–40 s ham sukut 60–90 s dan ancha yaxshi, tez-tez tekshirish esa CPU oladi.
Diqqat:
headersTimeout— bitta ulanishga qarshi himoya. Minglab ulanish bilan keladigan hujumni u to'xtatmaydi. Bunga qarshi ulanishlar sonini cheklash va proxy (nginxclient_header_timeout,limit_conn) kerak. Proxy ortidagi Node'ga Slowloris odatda yetib kelmaydi — proxy so'rovni to'liq olgandan keyin uzatadi.
3.4 requestTimeout — sekin tana
Sarlavhalar tez kelib, tana sekin kelsa-chi? Masalan, Content-Length: 100 va har 300 ms da bitta bayt. Bu holatni requestTimeout ushlaydi. Kanon: requestTimeout: 2000 — 2 049 ms da 408. vazifalar-api da 30 s: 16 KB lik JSON uchun bu juda ko'p, lekin sekin mobil internetdagi foydalanuvchini uzib qo'ymaydi.
Tekshirib ko'ring: Ishlovchingiz bazadan javobni 2 daqiqa kutadi,
requestTimeoutesa 30 s. Mijoz 408 oladimi?
Javob
Yo'q. requestTimeout mijoz so'rovni yuborib bo'lishi uchun vaqt. So'rov to'liq kelgach, taymer to'xtaydi. Ishlovchi qancha ishlashi uni qiziqtirmaydi. Bazaga so'rovga o'z timeout'ingizni qo'ying.
4. keepAliveTimeout va sirli 502
4.1 Keep-alive eslatmasi
HTTP server tomonda darsida ko'rgansiz: javobdan keyin ulanish yopilmaydi, keyingi so'rov uchun saqlanadi (Connection: keep-alive). Server javobga Keep-Alive: timeout=5 yozadi: "5 soniya bo'sh tursang — yopaman".
4.2 Node 24 da +1 soniya
Biz ishga tushirdik — keepAliveTimeout: 1000, xom soket bilan bitta so'rov, keyin jim kutamiz:
import { createServer } from "node:http";
import { connect } from "node:net";
const server = createServer({ keepAliveTimeout: 1000 },
(req, res) => res.end("ok"));
await new Promise((ok) => server.listen(0, "127.0.0.1", ok));
console.log("bufer:", server.keepAliveTimeoutBuffer, "ms");
const soket = connect(server.address().port, "127.0.0.1");
let javob = "";
let t0 = 0;
soket.on("data", (b) => {
javob += b;
if (javob.endsWith("ok")) t0 = performance.now(); // javob tugadi
});
soket.write("GET / HTTP/1.1\r\nHost: x\r\n\r\n");
await new Promise((ok) => soket.on("close", ok));
console.log(/Keep-Alive: .*/.exec(javob)[0].trim());
const s = Math.round((performance.now() - t0) / 1000);
console.log(`bo'sh ulanish ~${s} s dan keyin yopildi`);
server.closeAllConnections();
server.close();Konsolda:
bufer: 1000 ms
Keep-Alive: timeout=1
bo'sh ulanish ~2 s dan keyin yopildiServer mijozga "1 s" dedi, lekin o'zi 2 s da yopdi. Node 24 da keepAliveTimeoutBuffer (sukut 1000 ms; Node hujjatiga ko'ra v24.6.0 da qo'shilgan) qo'shiladi. Kanon aniq o'lchadi: 1000 → 2 014 ms, 3000 → 4 012 ms. Mantiq: mijoz "1 s" ni eshitib, o'zi o'z vaqtida yopsin. Server esa biroz ko'proq kutsin — shunda ikkalasi bir vaqtda harakat qilib to'qnashmaydi. Keyingi bo'limdagi muammo aynan shu to'qnashuv.
4.3 502 qanday tug'iladi
Load balancer (nginx, AWS ALB) ham Node'ga ulanishlarni qayta ishlatadi va ular uchun o'z bo'sh vaqti bor — odatda ~60 s. Node'da esa sukut 5 s. Endi vaqt chizig'i:
sequenceDiagram
participant LB as Load balancer (60 s)
participant N as Node (5 s)
LB->>N: so'rov 1
N-->>LB: 200
Note over N: 6 s bo'sh — Node ulanishni yopadi
LB->>N: so'rov 2 (LB uchun ulanish hali "tirik")
N--xLB: ulanish yopilgan (ECONNRESET)
Note over LB: mijozga 502 Bad GatewayLB ulanishni hali yaroqli deb hisoblaydi va yangi so'rovni yuboradi. Aynan shu millisekundda Node uni yopayotgan bo'ladi. So'rov bo'shliqqa ketadi, LB foydalanuvchiga 502 qaytaradi. Kamdan-kam va tasodifiy — shuning uchun topish qiyin.
Qoida: ulanishni har doim mijoz tomon birinchi yopsin. Node uchun mijoz — LB. Demak Node'ning keepAliveTimeout i LB'nikidan katta bo'lsin. vazifalar-api da 65 000 ms (60 + zaxira). nginx'ning keepalive_timeout sukuti 75 s, lekin u brauzer tomonidagi ulanish uchun. Upstream (Node) tomonida alohida sozlama bor — upstream blokidagi keepalive_timeout, sukuti 60 s (nginx hujjati). Kanonda nginx yo'q — bu qism sinalmagan, hujjat bo'yicha. Aniq qiymatni har doim o'z LB hujjatingizdan oling.
Tekshirib ko'ring:
keepAliveTimeout: 65_000qo'yildi. Har bo'sh ulanish endi 66 s xotira va fayl deskriptorini band qiladi. Bu xavfli emasmi?
Javob
Proxy ortida — yo'q. Node'ga faqat LB ulanadi, u esa o'nlab doimiy ulanishni qayta ishlatadi. Minglab brauzer to'g'ridan ulanadigan bo'lsa, uzun keep-alive resurs yeydi. Shuning uchun bu qiymat "proxy ortidagi Node" uchun to'g'ri, to'g'ridan internetga qaragan server uchun emas.
5. Serverni to'g'ri to'xtatish
5.1 server.close() nima qiladi
Graceful shutdown (ehtiyotkor to'xtash) — yangi ulanish qabul qilinmaydi, boshlangan so'rovlar oxirigacha bajariladi, keyin jarayon chiqadi. server.close(cb) birinchi qismini qiladi: tinglashni to'xtatadi. cb esa hamma ulanish yopilgach chaqiriladi.
Tuzoq — bo'sh keep-alive ulanishlar. Node hujjatiga ko'ra, Node 19 dan beri close() chaqirilgan paytda bo'sh turgan ulanishlarni o'zi yopadi. Lekin o'sha paytda ishlayotgan ulanish keyinroq bo'shasa — uni yopmaydi, kutadi. Kanon o'lchovi aynan shu holat: bitta 1.5 s lik so'rov davom etayotganda close() chaqirildi. Chiqishda har qatorning oxiridagi callback ... ms raqamiga qarang:
4a. close() dan keyin yangi ulanish — ECONNREFUSED
4a. faqat server.close(): uzoq so'rov — 200 uzoq, Connection: keep-alive (1509 ms); bo'sh keep-alive ulanish yopildi null; close() callback 7405 ms dan keyin
4b. close() dan keyin yangi ulanish — ECONNREFUSED
4b. close() + har 100 ms closeIdleConnections(): uzoq so'rov — 200 uzoq, Connection: keep-alive (1508 ms); bo'sh keep-alive ulanish yopildi null; close() callback 1414 ms dan keyinIkkala holatda yangi ulanish rad etildi va uzoq so'rov 200 bilan tugadi. Farq oxirida: faqat close() bilan callback 7 405 ms kutdi. Uzoq so'rov javobidan keyin ulanish bo'sh keep-alive ga aylandi va Node uni 5 + 1 s ushlab turdi. closeIdleConnections() ni har 100 ms chaqirsak — 1 414 ms, ya'ni uzoq so'rov tugashi bilan.
Ikkinchi yordamchi — closeAllConnections(): hamma, jumladan ishlayotgan ulanishlarni uzadi. U oxirgi chora: "10 s kutdim, endi majburan".
5.2 yop() funksiyasi
Uchala usulni birlashtiramiz. Biz ishga tushirdik — kanondagi yop() va testdagi stsenariy (keep-alive bo'sh ulanish + 300 ms lik so'rov):
import { createServer, request } from "node:http";
function yop(server, kutishMs) {
return new Promise((resolve) => {
const iv = setInterval(() => server.closeIdleConnections(), 100);
const majburiy = setTimeout(() => {
server.closeAllConnections();
tugat(false);
}, kutishMs);
function tugat(ozVaqtida) {
clearInterval(iv);
clearTimeout(majburiy);
resolve(ozVaqtida);
}
server.close(() => tugat(true));
server.closeIdleConnections();
});
}
const server = createServer((req, res) => {
const ms = req.url === "/uzoq" ? 300 : 0;
setTimeout(() => res.end("tugadi"), ms);
});
await new Promise((ok) => server.listen(0, "127.0.0.1", ok));
const asos = `http://127.0.0.1:${server.address().port}`;
await (await fetch(asos)).text(); // keep-alive ulanish bo'sh qoldi
const uzoq = fetch(`${asos}/uzoq`).then((r) => r.text());
await new Promise((ok) => setTimeout(ok, 50));
const t0 = performance.now();
const yopildi = yop(server, 2000);
request(asos).on("error", (x) => console.log("yangi:", x.code)).end();
console.log("uzoq so'rov:", await uzoq);
console.log("o'z vaqtida:", await yopildi);
console.log("1 s dan tez:", performance.now() - t0 < 1000);Konsolda:
yangi: ECONNREFUSED
uzoq so'rov: tugadi
o'z vaqtida: true
1 s dan tez: trueUch kafolat: yangi ulanish rad etildi (ECONNREFUSED — "ulanish rad etildi"), boshlangan so'rov tugadi, va bo'sh keep-alive ulanish uchun 6 s kutilmadi.
5.3 Signallar
Server qachon to'xtashi kerakligini tashqaridan signal aytadi (process va signallar darsi): Docker va systemd — SIGTERM, terminalda Ctrl+C — SIGINT. vazifalar-api ning server.ts ida:
let yopilmoqda = false;
async function toxta(signal: string): Promise<void> {
if (yopilmoqda) return;
yopilmoqda = true;
console.log(`${signal}: yangi ulanishlar qabul qilinmaydi, …`);
const ozVaqtida = await yop(server, 10_000);
console.log(ozVaqtida
? "to'xtadi" : "10 s o'tdi — majburan uzildi");
process.exitCode = ozVaqtida ? 0 : 1;
}
process.once("SIGTERM", () => void toxta("SIGTERM"));
process.once("SIGINT", () => void toxta("SIGINT"));process.exit() chaqirilmaydi — exitCode qo'yiladi, Node esa event loop bo'shagach o'zi chiqadi. Majburiy exit() hali yozilayotgan logni kesib qo'yishi mumkin. yopilmoqda bayrog'i ikkinchi signalda ikkinchi marta yopishni boshlamaslik uchun.
Halol eslatma: haqiqiy SIGTERM kanonda sinalmagan. Windows'da boshqa jarayondan yuborilgan signal Node'dagi ishlovchini chaqirmaydi (process darsida ko'rgansiz). Docker va Linux'da sinash — deploy qismida. Hozircha yop() testlar bilan tekshirilgan.
6. Hujumchi nigohi
| Hujum | Sukut sozlamada | Himoya |
|---|---|---|
| Slowloris (sekin sarlavha) | 60–90 s ushlaydi | headersTimeout 10 s; proxy |
| Sekin tana (R.U.D.Y.) | 300 s gacha | requestTimeout 30 s; tana hajmi chegarasi (tana o'qish darsi) |
| Minglab bo'sh ulanish | har biri resurs | proxy, ulanishlar chegarasi |
| Deploy paytidagi uzilish | yarim saqlangan ma'lumot | graceful shutdown, yop() |
R.U.D.Y. (R-U-Dead-Yet) — Slowloris'ning tana varianti: sarlavhalar normal, Content-Length katta, tana esa bittalab bayt. requestTimeout va tana chegarasi birga ishlaydi.
7. API qadami: timeout'lar va graceful shutdown (holat 07, commit)
Holat 05 da server.ts createServer va listen ni o'zi qilardi. Endi uni ikkiga ajratamiz.
src/ishga-tushir.ts — serverni yaratadi va tinglaydi (testlar ham shuni ishlatadi):
export async function ishgaTushir(
{ port = 0, host = "127.0.0.1" } = {},
) {
const server = createServer(
{
// LB (nginx, ALB) bo'sh ulanishni ~60 s ushlaydi — Node undan
// KEYIN yopsin, aks holda mijoz 502 oladi
keepAliveTimeout: 65_000,
// Slowloris: sarlavhalar 10 s ichida to'liq kelmasa — 408
headersTimeout: 10_000,
// butun so'rov (tana bilan) 30 s ichida
requestTimeout: 30_000,
},
ilovaYarat({ ombor: new XotiraOmbori(), publicPapka }),
);
await new Promise<void>((resolve, reject) => {
server.once("error", reject); // EADDRINUSE va h.k.
server.listen(port, host, () => {
server.off("error", reject);
resolve();
});
});
const { port: haqiqiy } = server.address() as AddressInfo;
return { server, manzil: `http://${host}:${String(haqiqiy)}` };
}(Kanon, izohlar qisqartirilgan; publicPapka asl kodda join(import.meta.dirname, "..", "public").) Ikki detal: listen xatosi (port band — EADDRINUSE) Promise'ni rad etadi, jim yiqilmaydi. PORT=0 bo'lsa, haqiqiy port address() dan olinadi.
src/yopish.ts — yuqoridagi yop(server, kutishMs) (TypeScript turlari bilan). src/server.ts — ishgaTushir + signallar.
Yangi test fayli — test/yopish.test.ts, unda ikki test. (1) bo'sh keep-alive + 300 ms so'rov: yangi ulanish ECONNREFUSED, so'rov tugaydi, yop() 1.5 s dan tez; (2) hech qachon javob bermaydigan ishlovchi — 200 ms da majburan uziladi, false, mijozda fetch failed.
Jonli tekshiruvda javob sarlavhasi ham o'zgardi (kanon, holat 07):
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Content-Length: 144
Location: /vazifalar/1
Connection: keep-alive
Keep-Alive: timeout=65Holat 05 da timeout=5 edi. Tekshiruv: tip 0, lint 0, testlar 7/7; sovuq start 171 ms; yuk (bitta mashina, bitta o'lchov) GET /vazifalar 2 669 req/s, GET /vazifalar/5 27 096 req/s — timeout'lar tezlikka ta'sir qilmadi.
git add .
git commit -m "Server timeout'lari (keepAliveTimeout 65 s, headersTimeout 10 s) va graceful shutdown"8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| LB ortida tasodifiy 502 | keepAliveTimeout < LB bo'sh vaqti |
Node'niki kattaroq (65 s) |
| Deploy'da 10 s "osilish" | close() keyin bo'shagan keep-alive'ni kutadi |
closeIdleConnections() |
| Deploy'da so'rovlar uzildi | process.exit() darhol |
yop() + exitCode |
headersTimeout 10 s, lekin 40 s ishlaydi |
tekshiruv oralig'i 30 s | kutilgan xulq; proxy |
Error: listen EADDRINUSE |
port band | boshqa port yoki eski jarayonni to'xtatish |
| Uzoq ishlovchi baribir to'xtamaydi | requestTimeout ishlovchini cheklamaydi |
ichki timeout (AbortSignal.timeout) |
listen EADDRINUSE: address already in use 127.0.0.1:3000 — "manzil allaqachon band". Odatda oldingi server hali ishlayapti.
9. Mashqlar
Yechimlar kurs/mashqlar/25/07-timeout/ da.
1-mashq (oson): to'ldiring
- Sarlavhalar o'z vaqtida kelmasa server qaytaradigan status —
- Bo'sh keep-alive ulanishlarni darhol yopadigan metod —
server.() - Node 24 da
keepAliveTimeoutga qo'shiladigan bufer — ms - LB ortida Node'ning
keepAliveTimeouti LB'nikidan bo'lishi kerak - Docker konteynerni to'xtatganda yuboradigan signal —
Yechim
- 408 Request Timeout.
- closeIdleConnections — ishlayotgan so'rovlarga tegmaydi.
- 1000 —
keepAliveTimeoutBuffer; 1 s → 2 s da yopiladi. - katta — ulanishni mijoz (LB) birinchi yopsin.
- SIGTERM — terminaldagi Ctrl+C esa
SIGINT.
2-mashq (o'rta): sozlama tekshiruvchisi
timeoutTekshir(sozlama, lbBoshVaqt) toza funksiyasini yozing. U ogohlantirishlar massivini qaytarsin: keepAliveTimeout ≤ lbBoshVaqt → "502 xavfi"; headersTimeout > requestTimeout → "headersTimeout requestTimeout dan katta"; headersTimeout > 30 000 → "Slowloris'ga sekin javob". Hammasi joyida bo'lsa — bo'sh massiv. Sukut qiymatlar (5 000, 60 000, 300 000) va kanon qiymatlari (65 000, 10 000, 30 000) bilan sinang; LB — 60 000. Saytda ham ishlaydi.
Yechim
function timeoutTekshir(s, lbBoshVaqt) {
const xatolar = [];
if (s.keepAliveTimeout <= lbBoshVaqt) xatolar.push("502 xavfi");
if (s.headersTimeout > s.requestTimeout) {
xatolar.push("headersTimeout requestTimeout dan katta");
}
if (s.headersTimeout > 30_000) {
xatolar.push("Slowloris'ga sekin javob");
}
return xatolar;
}
const sukut = {
keepAliveTimeout: 5_000,
headersTimeout: 60_000,
requestTimeout: 300_000,
};
const kanon = {
keepAliveTimeout: 65_000,
headersTimeout: 10_000,
requestTimeout: 30_000,
};
console.log(timeoutTekshir(sukut, 60_000));
console.log(timeoutTekshir(kanon, 60_000));Konsolda:
[ '502 xavfi', "Slowloris'ga sekin javob" ]
[]Sukut sozlamalar proxy ortida ikki muammo beradi. Kanon qiymatlari — toza.
3-mashq (qiyin): majburiy uzish
«yop() funksiyasi» bo'limidagi yop ni oling va ikkinchi kanon testini Node'da takrorlang: ishlovchi hech qachon javob bermaydi, yop(server, 200) chaqiriladi. Natija false bo'lsin, mijozdagi fetch esa TypeError bilan tugasin. Ikkalasini va o'tgan vaqtni (100 ms gacha yaxlitlab) chiqaring.
Yechim
import { createServer } from "node:http";
function yop(server, kutishMs) {
return new Promise((resolve) => {
const iv = setInterval(() => server.closeIdleConnections(), 100);
const majburiy = setTimeout(() => {
server.closeAllConnections();
tugat(false);
}, kutishMs);
function tugat(ozVaqtida) {
clearInterval(iv);
clearTimeout(majburiy);
resolve(ozVaqtida);
}
server.close(() => tugat(true));
server.closeIdleConnections();
});
}
const server = createServer(() => {}); // hech qachon javob bermaydi
await new Promise((ok) => server.listen(0, "127.0.0.1", ok));
const osilgan = fetch(`http://127.0.0.1:${server.address().port}`)
.catch((x) => x);
await new Promise((ok) => setTimeout(ok, 50));
const t0 = performance.now();
console.log("o'z vaqtida:", await yop(server, 200));
const ms = Math.round((performance.now() - t0) / 100) * 100;
console.log("o'tdi:", ms, "ms");
const x = await osilgan;
console.log(x.name, x.message);Konsolda:
o'z vaqtida: false
o'tdi: 200 ms
TypeError fetch failedIshlovchi javob bermaydi — ulanish "ishlayotgan", closeIdleConnections unga tegmaydi. 200 ms dan keyin closeAllConnections uni uzadi.
4-mashq: API qadami
src/ishga-tushir.ts, src/yopish.ts va signalli src/server.ts ni «API qadami» bo'limidagidek yozing; test/yopish.test.ts ga ikki testni qo'shing. npm run check — 7/7. Jonli sinang: npm start, keyin curl -i $A/vazifalar — Keep-Alive: timeout=65. Commit qiling.
Yechim
Kod — «API qadami» bo'limida va «yop() funksiyasi» bo'limida (TypeScript'da server: Server, kutishMs = 10_000, natija Promise<boolean>). Testlar — «yop() funksiyasi» bo'limidagi va 3-mashqdagi stsenariylar, assert.rejects(..., { code: "ECONNREFUSED" }) va assert.ok(t < 1500) bilan. Eski testlar ishgaTushir({ port: 0 }) orqali server oladi va oxirida yop(server, 2000) bilan yopadi.
10. Real ishda
- Kubernetes, Docker — konteyner to'xtatilganda
SIGTERM, keyin (sukut 30 s / 10 s)SIGKILL. Graceful shutdown shu oraliqqa sig'ishi kerak. - AWS ALB, GCP LB, nginx — har birining bo'sh ulanish vaqti bor; Node'niki undan katta. Bu xato haqida ko'p kompaniya blog yozgan.
- Express, Fastify, NestJS — hammasi ostida o'sha
node:httpserveri; timeout'lar xuddi shunday beriladi (app.listen()qaytargan serverga yokicreateServerga). - Intervyu: "load balancer ortida 502 sababi?", "graceful shutdown qanday yoziladi?", "Slowloris nima?".
Xulosa
headersTimeoutvarequestTimeout— mijoz yuborishi uchun vaqt; 408 bilan yopiladi; Slowloris va sekin tanaga qarshi.- Node muddatni har
connectionsCheckingInterval(30 s) da tekshiradi — 10 s timeout amalda 13 s chiqdi. keepAliveTimeout+ 1 s bufer (Node 24); proxy ortida LB'nikidan katta bo'lsin —vazifalar-apida 65 s.server.close()keyin bo'shagan keep-alive'ni kutadi (7.4 s);closeIdleConnections()bilan 1.4 s;closeAllConnections()— oxirgi chora.- Signal →
yop()→process.exitCode;process.exit()emas.
Keyingi dars: Reverse proxy orqasida Node — nginx va Caddy, X-Forwarded-For va soxta IP sarlavhasi xavfi.
Manbalar
- Node.js 24 hujjati —
http.createServeropsiyalari (keepAliveTimeout,keepAliveTimeoutBuffer,headersTimeout,requestTimeout,connectionsCheckingInterval),server.closeIdleConnections(),closeAllConnections(): https://nodejs.org/docs/latest-v24.x/api/http.html - RFC 9110 (2022-06) — 408 Request Timeout: https://www.rfc-editor.org/rfc/rfc9110#name-408-request-timeout
- nginx hujjati —
keepalive_timeout(sukut 75 s),client_header_timeout: https://nginx.org/en/docs/http/ngx_http_core_module.html · upstreamkeepalive_timeout(sukut 60 s, 1.15.3 dan): https://nginx.org/en/docs/http/ngx_http_upstream_module.html (2026-10-24 da ochildi; biz sinamadik) - Kanon:
vazifalar-apiholat 07 (ishga-tushir.ts,yopish.ts, testlar, jonli Slowloris 13 373 ms), timeout tajribasi (Node 24.21.0, Windows 11). Slowloris 2 s, keep-alive bufer vayop()bloklari — biz ishga tushirdik.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!