Mundarija (28)
- Bu darsda
- 1. Nega bu kerak?
- 1.1 Monolit va mikroservis
- 1.2 Narxi
- 2. Birinchi mikroservis
- 2.1 Server tomoni
- 2.2 Ikki xil xabar
- 3. ClientProxy — mijoz tomoni
- 3.1 HTTP gateway
- 3.2 send — sovuq Observable
- 3.3 emit — darhol ketadi
- 4. Pattern qanday solishtiriladi
- 5. Xatolar
- 5.1 Uch xil xato
- 5.2 Timeout
- 6. Tezlik
- 7. Hybrid ilova
- 8. JS modelida pattern va javob
- 9. Kanon tajribasi: nega loyihaga kirmaydi
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): timeout modeli
- 3-mashq (qiyin): javoblarni id bo'yicha moslash
- 12. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 13. Real ishda
- Xulosa
- Manbalar
Mikroservis asoslari NestJS'da: @MessagePattern, @EventPattern va ClientProxy
Qisqacha: Nest ilovasi HTTP'siz ham ishlay oladi — mikroservis sifatida: u xabarlarni transport (TCP, Redis, NATS, RabbitMQ, Kafka, gRPC) orqali qabul qiladi. Ikki xil xabar bor:
@MessagePattern— so'rov-javob (client.send()javob kutadi),@EventPattern— hodisa (client.emit()javob kutmaydi). Boshqa xizmatClientProxybilan gaplashadi. Controller, DI, pipe, guard — hammasi HTTP'dagidek. Lekin avval savol: sizga mikroservis kerakmi? Ko'pincha javob — yo'q.
Bu darsda
- Monolit va mikroservis farqini va mikroservisning haqiqiy narxini aytib bera olasiz.
createMicroservicebilan TCP xizmat yozib,@MessagePatternva@EventPatternhandler'larini qo'shasiz.ClientProxyningsend(sovuq) vaemit(darhol) farqini haqiqiy chiqishda ko'rasiz.RpcException, noma'lum pattern va timeout xatolarini o'qiysiz.- HTTP gateway va hybrid ilova yasaysiz.
Oldin bilishingiz kerak: Boshqa xabar broker'lari, Global enhancer'lar va bajarilish tartibi, Interceptor'lar va RxJS, GraphQL chuqur NestJS'da.
Oldingi ikki darsda GraphQL'ni ko'rdik. Endi Nest ekotizimining yana bir katta qismi — mikroservislar. Bu darsda asoslar va eng sodda transport — TCP. Keyingi darsda broker'li transportlar va ishonchlilik. Kanon tajribasida ham TCP sinalgan; brokerlar Docker talab qiladi va bizda sinalmagan.
1. Nega bu kerak?
1.1 Monolit va mikroservis
vazifalar-api — monolit (monolith): bitta jarayon, bitta kod bazasi, bitta deploy. Vazifalar, eksport, salomatlik — hammasi bir joyda. Modullar bir-birini oddiy metod chaqiruvi bilan chaqiradi: nanosekundlar, xatosiz.
Mikroservis (microservice) arxitekturasida tizim alohida dasturlarga bo'linadi: "vazifalar xizmati", "hisobot xizmati", "bildirishnoma xizmati". Har biri o'z jarayonida, ko'pincha o'z bazasi bilan, alohida deploy qilinadi. Ular bir-biriga tarmoq orqali xabar yuboradi. Bu va keyingi darslarda "xizmat" so'zi shunday alohida dasturni ham bildiradi — kontekstdan ajrating: VazifalarService — klass, "hisobot xizmati" — alohida jarayon.
O'xshatish: oilaviy oshxona va restoranlar majmuasi. Oilaviy oshxonada bitta oshpaz hamma narsani qiladi: tez, oddiy, gaplashish shart emas. Katta restoranlar majmuasida esa har oshxona alohida — novvoyxona, kabobxona, shirinliklar. Har biri mustaqil ishlaydi va yopiladi, lekin buyurtmalar kuryer bilan yuboriladi. Kuryer kechikishi, adashishi, yo'lda yo'qolishi mumkin.
1.2 Narxi
Mikroservisning foydasi — mustaqillik: har jamoa o'z xizmatini o'zi deploy qiladi, bitta xizmat yiqilsa boshqasi ishlaydi, yuk tushgan xizmatni alohida kengaytirish mumkin. Narxi esa katta:
| Monolit | Mikroservislar | |
|---|---|---|
| Modullararo chaqiruv | metod: nanosekund | tarmoq: millisekund, yiqilishi mumkin |
| Tranzaksiya | bitta baza tranzaksiyasi | taqsimlangan — outbox, saga |
| Deploy | bitta | har xizmat alohida, versiyalar mosligi |
| Xato qidirish | bitta log | ko'p xizmat bo'ylab kuzatuv (tracing) |
Ko'p jamoalar boshida mikroservisga o'tib, keyin afsuslanadi. Amaliy qoida: avval yaxshi bo'lingan monolit, mikroservis — aniq sabab bo'lganda (alohida jamoa, alohida yuk, alohida til). Bu mavzuni Modular monolit va hexagonal arxitektura NestJS'da va arxitektura qismidagi monolit, modular monolit, mikroservis darsida davom ettiramiz.
Bu darsda esa texnikani o'rganamiz: tanlov qilinganda Nest'da qanday yoziladi.
2. Birinchi mikroservis
2.1 Server tomoni
npm i --save-exact @nestjs/microservices@12.2.0Biz demo loyihada (NestJS 12.2.0) "hisobot xizmati" yozdik:
import { Controller, Module } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import {
EventPattern, MessagePattern, Payload, Transport,
} from "@nestjs/microservices";
import type { MicroserviceOptions } from "@nestjs/microservices";
@Controller()
class HisobotController {
@MessagePattern({ cmd: "statistika" })
statistika(@Payload() d: { egasi: number }) {
return { egasi: d.egasi, jami: 12, bajarilgan: 5 };
}
@EventPattern("vazifa.bajarildi")
bajarildi(@Payload() d: { id: number }) {
jurnal.push(`hodisa: vazifa.bajarildi ${d.id}`);
}
}
@Module({ controllers: [HisobotController] })
class HisobotModule {}
const mikro = await NestFactory
.createMicroservice<MicroserviceOptions>(HisobotModule, {
transport: Transport.TCP,
options: { host: "127.0.0.1", port: 4591 },
});
await mikro.listen();HTTP ilovadan farqlar:
NestFactory.createMicroservice—createo'rnida. Ilova HTTP port ochmaydi, transport serverini ochadi (bu yerda TCP 4591).- Controller'da
@Get/@Posto'rnida@MessagePatternva@EventPattern. Controller — o'sha@Controller()klassi. @Payload()— xabar tanasi (HTTP'dagi@Body()ning o'rnidosh).
Request lifecycle darsidagi zanjir mikroservisda ham deyarli shunday. Bitta farq bor — middleware HTTP'ga xos, transport xabariga u ishlamaydi:
| Qadam | HTTP so'rovda | Mikroservis xabarida |
|---|---|---|
| middleware | ishlaydi | yo'q |
| guard | ishlaydi | ishlaydi (switchToRpc()) |
| interceptor (oldin) | ishlaydi | ishlaydi |
| pipe | @Body(), @Param() |
@Payload() |
| handler | @Get, @Post |
@MessagePattern, @EventPattern |
| interceptor (keyin) | ishlaydi | ishlaydi |
| xato filtri | HttpException |
RpcException («Xatolar» bo'limi) |
Guard ichida HTTP so'rovi o'rniga xabar ma'lumoti olinadi: kontekst.switchToRpc().getData() — Guard'lar darsidagi switchToHttp() ning transport varianti.
Pattern (naqsh) — xabarning "manzili". HTTP'da manzil — metod va yo'l (GET /statistika), mikroservisda — pattern: obyekt ({ cmd: "statistika" }) yoki satr ("vazifa.bajarildi").
2.2 Ikki xil xabar
@MessagePattern |
@EventPattern |
|
|---|---|---|
| Ma'nosi | "bajar va javob ber" | "shunday bo'ldi" |
| Mijoz metodi | client.send() |
client.emit() |
| Javob | bor | yo'q |
| Handler topilmasa | mijoz xato oladi | server logida xato |
Birinchisi — so'rov-javob (request-response): "statistikani hisobla". Ikkinchisi — hodisa (event-based): "vazifa bajarildi — kimga kerak bo'lsa, o'zi biladi" — Hodisalar darsidagi domen hodisasi, faqat endi jarayonlar orasida.
3. ClientProxy — mijoz tomoni
3.1 HTTP gateway
Odatda tashqi dunyo (brauzer, mobil ilova) mikroservis bilan to'g'ridan gaplashmaydi. Oldida HTTP API turadi — API gateway: u HTTP so'rovni qabul qiladi va kerakli xizmatga xabar yuboradi.
import { ClientsModule, Transport } from "@nestjs/microservices";
import type { ClientProxy } from "@nestjs/microservices";
@Controller()
class GatewayController {
constructor(
@Inject("HISOBOT") private readonly hisobot: ClientProxy,
) {}
@Get("statistika")
stat() {
return this.hisobot.send({ cmd: "statistika" }, { egasi: 7 });
}
}
@Module({
imports: [
ClientsModule.register([{
name: "HISOBOT", transport: Transport.TCP,
options: { host: "127.0.0.1", port: 4591 },
}]),
],
controllers: [GatewayController],
})
class GatewayModule {}ClientsModule.register — dinamik modul: HISOBOT tokeni ostida ClientProxy provider'ini yaratadi. Sozlama config'dan kelishi kerak bo'lsa — registerAsync (useFactory + inject: [ConfigService]). send Observable qaytaradi. Controller uni to'g'ridan qaytardi — Nest HTTP Observable'ni kutib, natijani javob qiladi (Interceptor'lar va RxJS).
Ikki ilovani ishga tushirib, HTTP so'rov yubordik (biz ishga tushirdik):
1) HTTP → gateway → TCP
GET /statistika → 200 {"egasi":7,"jami":12,"bajarilgan":5}Zanjir: fetch → gateway (HTTP) → send → TCP → hisobot xizmati → javob → TCP → gateway → HTTP javob.
flowchart LR
B["Brauzer"] -->|"HTTP GET"| G["API gateway (Nest HTTP)"]
G -->|"TCP send cmd statistika"| H["Hisobot xizmati"]
H -->|"javob"| G
V["Vazifalar xizmati"] -.->|"TCP emit vazifa.bajarildi"| HDiagrammada uzluksiz strelkalar — so'rov-javob (javob qaytadi), uzuq strelka — hodisa (javob yo'q).
3.2 send — sovuq Observable
Muhim nozik joy. send qaytargan Observable sovuq (cold): obuna bo'lmaguncha hech narsa yuborilmaydi. Biz xizmatdagi handler'ga jurnal qo'ydik va tekshirdik:
const kuzatiladi = mijoz.send({ cmd: "statistika" }, { egasi: 1 });
await kut(100);
console.log("subscribe oldidan jurnal:", JSON.stringify(jurnal));
const natija = await firstValueFrom(kuzatiladi);
console.log("natija:", JSON.stringify(natija));
console.log("subscribe dan keyin:", JSON.stringify(jurnal));2) send — "sovuq" Observable
subscribe oldidan jurnal: []
natija: {"egasi":1,"jami":12,"bajarilgan":5}
subscribe dan keyin: ["statistika(1)"]100 ms kutdik — xizmat hech narsa olmadi. firstValueFrom obuna bo'lgandagina xabar ketdi. Tuzoq: this.hisobot.send(...) ni yozib, natijani qaytarmasangiz yoki await firstValueFrom qilmasangiz — xabar umuman yuborilmaydi. Xato ham, ogohlantirish ham yo'q.
3.3 emit — darhol ketadi
3) emit — subscribe siz ham ketadi
jurnal: ["hodisa: vazifa.bajarildi 7"]
ERROR [Server] There is no matching event handler defined in the remote service. Event pattern: vazifa.ochirildiemit obunasiz ham yubordi — Nest uni darhol ulaydi (hot). Ikkinchi emit — vazifa.ochirildi, unga handler yo'q. Mijoz hech narsa sezmadi. Xatoni xizmat o'z logiga yozdi: "masofaviy xizmatda mos hodisa handler'i yo'q. Hodisa pattern'i: vazifa.ochirildi". Hodisa yo'qoldi. Bu "fire-and-forget" ning tabiati: kafolat kerak bo'lsa, broker va tasdiqlash (ack) kerak — keyingi dars.
Tekshirib ko'ring: Controller metodida
this.hisobot.send({ cmd: "hisobla" }, d);deb yozildi, qaytarilmadi. Metod{ ok: true }qaytaradi. Hisobot xizmati hisoblaydimi?
Javob
Yo'q. send sovuq Observable qaytaradi va unga hech kim obuna bo'lmadi — xabar tarmoqqa chiqmadi. Agar javob kerak bo'lmasa, emit ishlating (u darhol ketadi). Agar javob kerak bo'lsa, return this.hisobot.send(...) yoki await firstValueFrom(this.hisobot.send(...)).
4. Pattern qanday solishtiriladi
Obyekt pattern'da kalitlar tartibi muhimmi? Xizmatdagi handler { cmd: "ikki", v: 1 } edi, biz { v: 1, cmd: "ikki" } yubordik. Yana @Ctx() bilan xizmat pattern'ni qanday ko'rishini so'radik:
@MessagePattern({ cmd: "kim" })
kim(@Ctx() ctx: TcpContext) {
return ctx.getPattern();
}4) kalitlar tartibi
ikki kalitli pattern
"{\"cmd\":\"kim\"}"Tartib muhim emas. Nest pattern obyektini satrga aylantiradi va kalitlarni alifbo bo'yicha saralaydi (@nestjs/microservices manbasidagi transformPatternToRoute). Ichkarida pattern — {"cmd":"kim"} satri, handler'lar shu satr bo'yicha Map da turadi. @Ctx() — transport konteksti: TCP'da TcpContext (pattern, soket), Kafka'da KafkaContext (partition, offset) va hokazo transportga xos ma'lumot.
5. Xatolar
5.1 Uch xil xato
@MessagePattern({ cmd: "topilmadi" })
topilmadi() {
throw new RpcException({
kod: "vazifa-topilmadi", xabar: "Vazifa 99 topilmadi",
});
}
@MessagePattern({ cmd: "buzuq" })
buzuq() {
throw new Error("ombor uzildi");
}Mijoz uchala pattern'ni (topilmadi, buzuq va umuman yo'q yoq) chaqirdi. Chiqishda → dan keyingi qism — mijoz nima olgani, ERROR bilan boshlangan qator — xizmatning o'z logi:
5) xatolar
topilmadi → {"kod":"vazifa-topilmadi","xabar":"Vazifa 99 topilmadi"}
ERROR [RpcExceptionsHandler] Error: ombor uzildi
buzuq → {"status":"error","message":"Internal server error"}
yoq → "There is no matching message handler defined in the remote service."RpcException— mikroservisningHttpExceptioni. Unga berilgan obyekt mijozga aynan yetib keldi. Mijoz xatoning turini shu obyektdan biladi.- Oddiy
Error— xizmat uni logga yozdi (RpcExceptionsHandler), mijoz esa faqat{"status":"error","message":"Internal server error"}oldi. Ichki tafsilot sizmadi — HTTP'dagi xavfsiz xulq bilan bir xil. - Noma'lum pattern — mijoz satr oldi: "masofaviy xizmatda mos xabar handler'i aniqlanmagan". Kanon tajribasida ham aynan shu matn.
HTTP'dagidek global xato filtri ham yozish mumkin — @Catch(RpcException) va BaseRpcExceptionFilter.
5.2 Timeout
await firstValueFrom(
mijoz.send({ cmd: "sekin" }, {}).pipe(timeout(100)),
); sekin + timeout(100) → TimeoutErrorXizmat 500 ms uxladi, mijoz 100 ms dan keyin rxjs TimeoutError oldi. Har send ga timeout qo'ying. Usiz xizmat osilsa, gateway'ning HTTP so'rovi ham osiladi, keyin hovuz to'ladi va zanjir bo'ylab hamma narsa to'xtaydi. Mikroservis tizimlarida bunday "zanjirli yiqilish" (cascading failure) eng ko'p uchraydigan halokat.
Timeout so'rovni bekor qilmaydi: xizmat baribir 500 ms ishlab, javob yuboradi — mijoz uni tashlab yuboradi. Xizmat tomonda og'ir ish bo'lsa, buni hisobga oling.
6. Tezlik
Kanon tajribasi TCP'da 1 000 ta ketma-ket so'rov-javobni o'lchagan. Biz ham qayta o'lchadik va parallel variantni qo'shdik:
6) 1000 ketma-ket so'rov-javob
o'rtacha 0.100 ms
1000 parallel: 48 msNima bo'ldi? Ketma-ket yuborganda har so'rov oldingisining javobini kutdi — o'rtacha 0.1 ms. Parallel yuborganda 1 000 ta so'rov bitta ulanishda bir vaqtda "havoda" turdi va hammasi 48 ms da qaytdi. Bu qanday ishlashini 3-mashqda modellaymiz.
Kanon: birinchi so'rov 5.4 ms (ulanish), keyingilari o'rtacha 0.118 ms. Bizda 0.100 ms. Bitta mashina, ikkalasi ham 127.0.0.1 — haqiqiy tarmoqsiz. Haqiqiy tarmoqda (ikki server, bitta ma'lumotlar markazi) bunga yana 0.2–1 ms qo'shiladi.
Bu raqamni metod chaqiruvi bilan solishtiring: oddiy JS funksiya chaqiruvi nanosekundlarda o'lchanadi — kamida mingdan bir necha baravar tezroq. Monolit ichidagi VazifalarService.ol() ni mikroservisga ko'chirsangiz, har chaqiruv shuncha qimmatlashadi va yangi xato turlari paydo bo'ladi (timeout, ulanish uzilishi). Bu «Narxi» bo'limidagi jadvalning raqamdagi ifodasi.
7. Hybrid ilova
Bitta Nest ilovasi bir vaqtda ham HTTP, ham mikroservis bo'la oladi — hybrid ilova:
const app = await NestFactory.create(HisobotModule);
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: { host: "127.0.0.1", port: 4592 },
});
await app.startAllMicroservices();
await app.listen(0, "127.0.0.1");HisobotController ga @Get("holat") ham qo'shdik — bitta controller'da HTTP marshrut va pattern handler'lari:
7) hybrid ilova
TCP: {"egasi":3,"jami":12,"bajarilgan":5}
HTTP GET /holat → 200 {"ok":true}Ikki transport — bitta DI konteyner, bitta xizmatlar to'plami. Hybrid ilova asta-sekin ko'chish uchun qulay. Monolit HTTP'da ishlashda davom etadi va yangi xizmatlar bilan TCP yoki broker orqali gaplasha boshlaydi. connectMicroservice ni bir necha marta chaqirib, bir nechta transport ulash mumkin. Rasmiy hujjatga ko'ra (Hybrid application sahifasi, 2026-11-02 holatiga), ulangan mikroservis global pipe, guard, interceptor va xato filtrlarini sukut bo'yicha olmaydi — useGlobal*() bilan ulanganini ham, APP_PIPE, APP_GUARD, APP_INTERCEPTOR, APP_FILTER provider'lari bilan ulanganini ham. Meros olish uchun connectMicroservice(…, { inheritAppConfig: true }) kerak, useGlobal*() chaqiruvlari esa connectMicroservice dan oldin turishi lozim (biz buni sinamadik).
8. JS modelida pattern va javob
Nest mikroservisining ichidagi uch g'oyani paketsiz modelda ko'raylik: pattern'ni satrga aylantirish (kalitlar saralangan), so'rov-javobda id, hodisada id yo'q. "Tarmoq"ni JSON.parse(JSON.stringify(...)) bilan modellaymiz — har xabar seriyalanadi. Blok brauzerda ishlaydi:
// Nest mikroservis g'oyasi: pattern → handler, so'rov-javob va hodisa
const kalit = (p) => typeof p === "string" ? p : JSON.stringify(
Object.fromEntries(Object.entries(p).toSorted(
([a], [b]) => a.localeCompare(b),
)),
);
const tarmoq = (x) => JSON.parse(JSON.stringify(x)); // seriyalash
class Server {
#xabarlar = new Map();
#hodisalar = new Map();
messagePattern(p, fn) { this.#xabarlar.set(kalit(p), fn); }
eventPattern(p, fn) { this.#hodisalar.set(kalit(p), fn); }
qabul({ pattern, data, id }) {
if (id === undefined) { // hodisa: javob yo'q
const fn = this.#hodisalar.get(pattern);
if (!fn) console.log(`ERROR noma'lum hodisa: ${pattern}`);
else fn(data);
return undefined;
}
const fn = this.#xabarlar.get(pattern);
if (!fn) {
return { id, err: "There is no matching message handler" };
}
return { id, response: fn(data) };
}
}
class Mijoz {
#id = 0;
constructor(server) { this.server = server; }
async send(p, data) {
const paket = tarmoq({ pattern: kalit(p), data, id: ++this.#id });
const javob = tarmoq(this.server.qabul(paket));
if (javob.err) throw new Error(javob.err);
return javob.response;
}
emit(p, data) {
this.server.qabul(tarmoq({ pattern: kalit(p), data }));
}
}
const s = new Server();
s.messagePattern({ cmd: "statistika", v: 1 }, (d) => ({
egasi: d.egasi, jami: 12, vaqt: new Date(0),
}));
s.eventPattern("vazifa.bajarildi", (d) => {
console.log(`hodisa: ${d.id}`);
});
const m = new Mijoz(s);
console.log(await m.send({ v: 1, cmd: "statistika" }, { egasi: 7 }));
m.emit("vazifa.bajarildi", { id: 7 });
m.emit("vazifa.ochirildi", { id: 7 });
await m.send({ cmd: "yoq" }, {}).catch((e) => console.log(e.message));Konsolda:
{ egasi: 7, jami: 12, vaqt: '1970-01-01T00:00:00.000Z' }
hodisa: 7
ERROR noma'lum hodisa: vazifa.ochirildi
There is no matching message handlerBirinchi qatordagi vaqt ga qarang: xizmat Date qaytardi, mijoz satr oldi. Tarmoqdan faqat JSON o'tadi — sana, Map, klass nusxasi, BigInt yo'qoladi yoki xato beradi. Nest'ning TCP transporti ham JSON ishlatadi. Shuning uchun xizmatlar orasidagi shartnomani (DTO) oddiy JSON turlarida yozing. Qolgan qatorlar Nest chiqishi bilan bir xil ma'noda: { v: 1, cmd } kalitlar tartibidan qat'i nazar topildi, noma'lum hodisa — xizmat logida, noma'lum xabar — mijozda xato.
Soddalashtirilgan joyi: haqiqiy transport asinxron va javoblar tartibsiz kelishi mumkin — shuning uchun id kerak (3-mashq).
9. Kanon tajribasi: nega loyihaga kirmaydi
Kanon vazifalar-api ni xizmatlarga bo'lmadi. Tajribada (@nestjs/microservices 12.2.0, TCP, brokersiz) xuddi shu darsdagi beshta narsa tekshirilgan. Chiqishni darsdagi demo bilan solishtiring — raqamlar yaqin, xatolar matni aynan:
2) TCP mikroservis: send → {"egasi":1,"jami":12,"bajarilgan":5} (5.4 ms, birinchi)
1000 ketma-ket so'rov-javob: o'rtacha 0.118 ms
[mikroservis] voqea qabul qilindi: {"id":7}
timeout(100) → TimeoutError
noma'lum pattern → "There is no matching message handler defined in the remote service."(Kanon chiqishidagi "voqea" — tajriba faylidagi matn; kursda biz "hodisa" deymiz.) Sabablar oddiy: bitta jamoa, bitta frontend, bitta baza, yuk kichik. Mikroservisning birorta foydasi ishlamaydi, narxi esa to'liq to'lanadi: taqsimlangan tranzaksiya, har chaqiruvda tarmoq, ko'p deploy. 29-qismda qo'shilgan ishchi jarayon (BullMQ worker) — loyihadagi yagona "alohida jarayon", u ham navbat orqali, mikroservis transporti emas.
10. Ko'p uchraydigan xatolar
| Xato | Nima bo'ladi | Tuzatish |
|---|---|---|
send ga obuna yo'q |
xabar umuman ketmaydi | return yoki firstValueFrom |
send da timeout yo'q |
xizmat osilsa, gateway ham osiladi | .pipe(timeout(ms)) |
Oddiy Error dan aniq xabar kutish |
mijoz Internal server error oladi |
RpcException |
Javob kerak, lekin emit |
javob yo'q, xato ham yo'q | send |
Date/Map ni tarmoqdan o'tkazish |
satr yoki bo'sh obyekt keladi | JSON turlari |
| Mikroservis "zamonaviy" deb | murakkablik, sekinlik | avval modular monolit |
11. Mashqlar
Mashqlar kurs/mashqlar/30/45-mikroservis/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- So'rov-javob handler'i dekoratori —
- Hodisa yuboradigan
ClientProxymetodi — - Obuna bo'lmaguncha xabar yubormaydigan metod —
- Mijozga aniq xato obyektini yetkazadigan istisno klassi —
- HTTP va mikroservisni bitta ilovada birlashtirish — ilova
Yechim
- @MessagePattern —
@MessagePattern({ cmd: "statistika" }); hodisa uchun@EventPattern. - emit — darhol ketadi, javob yo'q.
- send — sovuq Observable;
firstValueFrombilan obuna. - RpcException — oddiy
ErroresaInternal server errorga aylanadi. - hybrid —
connectMicroservice+startAllMicroservices.
2-mashq (o'rta): timeout modeli
vaqtCheklovi(promise, ms) funksiyasini yozing: ms ichida natija kelmasa, name i "TimeoutError" bo'lgan xato bilan rad etsin (rxjs timeout dagidek). Taymerni natija kelganda ham tozalang. Ikki "xizmat" bilan sinang: tez (5 ms) va sekin (200 ms), chegara 50 ms. Ishora: Promise.race (Promise kombinatorlari) va finally.
Yechim
function vaqtCheklovi(promise, ms) {
let t;
const muddat = new Promise((_, rad) => {
t = setTimeout(() => {
const x = new Error("Timeout has occurred");
x.name = "TimeoutError";
rad(x);
}, ms);
});
return Promise.race([promise, muddat])
.finally(() => clearTimeout(t));
}
const kut = (ms, q) =>
new Promise((ok) => setTimeout(() => ok(q), ms));
const xizmatlar = {
sekin: () => kut(200, "kech"),
tez: () => kut(5, "tez"),
};
for (const nom of ["tez", "sekin"]) {
try {
console.log(nom, "→", await vaqtCheklovi(xizmatlar[nom](), 50));
} catch (e) {
console.log(nom, "→", e.name);
}
}Konsolda:
tez → tez
sekin → TimeoutError"Timeout has occurred" — rxjs'dagi TimeoutError ning xabari. finally siz tez holatida taymer 50 ms osilib turardi. Darsdagi kabi: sekin xizmat baribir 200 ms ishladi — timeout uni to'xtatmaydi, faqat kutmaydi.
3-mashq (qiyin): javoblarni id bo'yicha moslash
Bitta TCP ulanish orqali uchta so'rov ketdi, javoblar esa boshqa tartibda qaytdi (har biri har xil vaqt ishladi). Mijoz qaysi javob qaysi so'rovniki ekanini qanday biladi? Mijoz klassini yozing: send(data) har so'rovga yangi id beradi va Map da id → resolve saqlaydi; javob kelganda id bo'yicha to'g'ri Promise ni hal qiladi. Server a ni 30 ms, b ni 5 ms, c ni 15 ms da qaytarsin. Ishora: #kutilmoqda — Map; Promise.all natijasi so'rovlar tartibida.
Yechim
// Javoblar tartibsiz keladi — mijoz ularni id bo'yicha moslaydi
const kut = (ms) => new Promise((ok) => setTimeout(ok, ms));
function server(paket, javobBer) {
const kechikish = { a: 30, b: 5, c: 15 }[paket.data];
kut(kechikish).then(() => javobBer({
id: paket.id, response: paket.data.toUpperCase(),
}));
}
class Mijoz {
#id = 0;
#kutilmoqda = new Map(); // id → resolve
send(data) {
const id = ++this.#id;
return new Promise((ok) => {
this.#kutilmoqda.set(id, ok);
server({ id, data }, (j) => this.#keldi(j));
});
}
#keldi({ id, response }) {
console.log(`keldi: id=${id}`);
this.#kutilmoqda.get(id)(response);
this.#kutilmoqda.delete(id);
}
}
const m = new Mijoz();
const sorovlar = ["a", "b", "c"].map((x) => m.send(x));
const natija = await Promise.all(sorovlar);
console.log(natija);Konsolda:
keldi: id=2
keldi: id=3
keldi: id=1
[ 'A', 'B', 'C' ]Javoblar 2, 3, 1 tartibida keldi, natija esa to'g'ri: A, B, C. Nest'ning TCP mijozi aynan shunday ishlaydi: har paketda id, javobda o'sha id. Darsdagi "1000 parallel: 48 ms" o'lchovi shu mexanizm tufayli mumkin — bitta ulanishda minglab so'rov bir vaqtda "havoda" turadi. delete muhim: usiz Map har so'rov bilan o'sib boradi (xotira sizishi).
12. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
vazifalar-api o'zgarmaydi. Mashq sifatida: o'z demo loyihangizda VazifalarService ning statistikasini alohida "hisobot" xizmatiga chiqarib, gateway'dan send + timeout bilan chaqiring. Keyin hisobot xizmatini o'chirib, gateway nima qaytarishini ko'ring — bu mikroservis dunyosidagi birinchi "xizmat yo'q" holati. Keyingi darsda broker transportlar shu holatni qanday yumshatishini ko'ramiz.
13. Real ishda
- Qayerda uchraydi: katta e-commerce, bank va taksi platformalarida — "buyurtma", "to'lov", "bildirishnoma" alohida xizmatlar. Nest mikroservislari Node ekotizimida eng mashhur yechimlardan biri.
- Hybrid — eng ko'p uchraydigan real holat: HTTP API + RabbitMQ yoki Kafka'dan hodisalar tinglash bitta ilovada.
- Intervyu savollari: "Monolit va mikroservis — qachon nima?", "
sendvaemitfarqi?", "Mikroservis chaqiruvida timeout nega shart?", "API gateway nima?". - Kod review'da: "bu
sendga timeout qo'yilganmi?" va "bu xato mijozga nima bo'lib boradi?".
Xulosa
- Mikroservis — tarmoq orqali gaplashadigan alohida jarayonlar; foydasi — mustaqillik, narxi — tarmoq, taqsimlangan tranzaksiya, murakkablik.
createMicroservice+@MessagePattern(so'rov-javob) va@EventPattern(hodisa); pattern kalitlari saralangan satrga aylanadi.ClientProxy.send— sovuq, obunasiz ketmaydi;emit— darhol, javobsiz; noma'lum hodisa xizmat logida qoladi.RpcException— aniq xato mijozga; oddiyError—Internal server error; harsendgatimeout.- TCP: ~0.1 ms bir so'rov (lokal) — metod chaqiruvidan ming baravarlab sekin; hybrid ilova — HTTP + transport bir jarayonda.
Keyingi dars: Transport'lar chuqur — Redis, NATS, RabbitMQ, Kafka va gRPC transportlari, serializer, retry va ishonchlilik sozlamalari.
Manbalar
- NestJS hujjati — Microservices Overview, Basics (TCP,
ClientsModule,send/emit): https://docs.nestjs.com/microservices/basics - NestJS hujjati — Hybrid application: https://docs.nestjs.com/faq/hybrid-application
- NestJS hujjati — Exception filters (microservices),
RpcException: https://docs.nestjs.com/microservices/exception-filters - Martin Fowler, "MonolithFirst": https://martinfowler.com/bliki/MonolithFirst.html
- Kanon (IlmHamroh, 2026-10-28): TCP mikroservis tajribasi — birinchi so'rov 5.4 ms, 1 000 ketma-ket 0.118 ms,
TimeoutError, noma'lum pattern. Demo (gateway, sovuqsend,emit, pattern tartibi,RpcException, 1 000 parallel, hybrid) — biz ishga tushirdik (2026-11-02, Node 24.21.0, NestJS 12.2.0,@nestjs/microservices12.2.0; bitta mashina,127.0.0.1).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!