8.16-bob: Microservices — NestJS mikroservislar
8-QISM — NestJS (chuqur) · 16-mavzu
1. Kirish va motivatsiya
Production poydevorini 8.15-bob bildik. Endi NestJS'ning ilg'or arxitektura mavzusiga — mikroservislar ga — o'tamiz. Shu paytgacha monolit (yaxlit ilova — barcha modul bitta jarayonda) qurdik. Mikroservis — ilovani mustaqil, kichik xizmatlarga bo'lish (har biri o'z vazifasi, o'z DB'si, alohida deploy). Bu — katta, masshtablanadigan tizimlar (Netflix, Uber, Amazon) arxitekturasi. NestJS bu mavzuda ham kuchli — @nestjs/microservices paketi TCP, Redis, RabbitMQ, Kafka, gRPC, NATS transport'larini bir xil kod bilan beradi.
Avval muhim haqiqat: mikroservis — har doim to'g'ri tanlov emas. Ko'p loyiha uchun monolit yetarli (oddiyroq, tezroq). Mikroservis murakkablik qo'shadi (tarmoq, taqsimlangan tranzaksiya, monitoring). Lekin katta jamoa, mustaqil masshtab (bir qism og'ir yuklangan), turli texnologiya kerak bo'lganda — mikroservis. Bu bobda ikkalasini ham ko'rib chiqamiz: qachon monolit, qachon mikroservis, va NestJS'da mikroservis qanday quriladi.
Bu bob: monolit vs mikroservis (qachon qaysi), NestJS transport'lar (TCP/Redis/RabbitMQ/Kafka/gRPC), message pattern (so'rov-javob) vs event pattern (fire-and-forget), ClientProxy (xizmatlararo aloqa), API Gateway, hybrid app, va amaliy naqshlar — chuqur. Bu bob 9-QISM (arxitektura) va 10-QISM (DevOps) bilan bog'liq. Bu — senior darajadagi arxitektura bilimi.
O'xshatish: monolit — bitta katta restoran (oshpaz, ofitsiant, kassir — bir bino, bir jamoa — oson boshqarish, lekin kengaytirish qiyin). Mikroservis — ko'cha taomlari bozori (har kiosk mustaqil — biri lavash, biri choy, biri shashlik — har biri alohida ishlaydi, alohida kengayadi, biri yopilsa boshqasi ishlaydi). Kioskar bir-biri bilan buyurtma chiplari orqali gaplashadi (message/event — transport). API Gateway — bozor kirishidagi yagona ma'lumot punkti (mijoz bitta joyga boradi, u kerakli kioskka yo'naltiradi). Bozor — moslashuvchan, lekin boshqarish murakkab (monolit restorandan ko'ra).
Nega muhim?
- Masshtab — har xizmat mustaqil kengayadi (katta tizim).
- Mustaqil deploy — bir xizmat o'zgarsa, boshqasi tegmaydi.
- Senior bilim — arxitektura tanlovi (monolit vs mikroservis).
- NestJS kuchi — transport-agnostik (bir kod, ko'p transport).
2. Nazariya — chuqur tushuntirish
2.1. Monolit vs Mikroservis (qachon qaysi)
MONOLIT (bitta ilova):
Oddiy (bir kod, bir deploy, bir DB)
Tez boshlash (ko'p loyiha uchun yetarli)
Oson debug/test (bir jarayon)
Katta bo'lsa — og'ir; butun masshtab; bir xato hammasi
MIKROSERVIS (ko'p mustaqil xizmat):
Mustaqil masshtab (faqat og'ir qism)
Mustaqil deploy/texnologiya; xato izolyatsiyasi
Murakkab (tarmoq, taqsimlangan tranzaksiya, monitoring)
Operatsion og'irlik (ko'p deploy, kuzatuv)
QOIDA: Monolit'dan boshlang kerak bo'lganda ajrating ("monolith first")Monolit vs Mikroservis (eng muhim qaror): monolit — oddiy, ko'p loyiha uchun yetarli (boshlash uchun ideal); mikroservis — katta jamoa/masshtab/mustaqillik kerak bo'lganda. "Monolith first" (Martin Fowler) — monolitdan boshlang, o'sganda ajrating. Mikroservisni erta tanlash — keraksiz murakkablik. Bu — senior arxitektura mulohazasi (9-QISM).
2.2. NestJS mikroservis asosi
// Mikroservis yaratish (HTTP emas — transport)
import { NestFactory } from "@nestjs/core";
import { Transport } from "@nestjs/microservices";
async function bootstrap() {
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.TCP, // transport (2.4)
options: { host: "0.0.0.0", port: 3001 },
});
await app.listen();
}
bootstrap();NestJS mikroservis —
createMicroservice(HTTP server emas, transport tinglovchi). Transport (TCP/Redis/...). Mikroservis — controller o'rniga message handler (@MessagePattern— 2.5). Bir xil NestJS (modul, DI, service) — faqat kirish HTTP emas, message. Bu —@nestjs/microservicesasosi.
2.3. Transport'lar (taqqoslash — muhim jadval)
┌──────────┬─────────────────────────────────────────────┐
│ TCP │ Eng oddiy (tug'ma). O'rganish uchun. Kichik. │
│ Redis │ Pub/Sub. Yengil. Tez. (5.21 Redis) │
│ RabbitMQ │ Navbat (queue). Routing, dead-letter, retry. │
│ │ Vazifa qayta ishlash uchun (task) │
│ Kafka │ Event streaming. Durable, replay, partition. │
│ │ Event manba (event sourcing). Og'ir. │
│ gRPC │ Tez, tipli (protobuf). Xizmatlararo RPC. │
│ NATS │ Yengil, tez pub/sub. │
└──────────┴─────────────────────────────────────────────┘Transport tanlovi: TCP (oddiy — o'rganish), Redis (yengil pub/sub — 5.21), RabbitMQ (navbat — vazifa, routing, retry, dead-letter), Kafka (event streaming — durable, replay — event manba bo'lsa), gRPC (tez, tipli — xizmatlararo RPC). NestJS — bir xil kod, transport almashtirsa kod o'zgarmaydi (abstraksiya). Tanlov ehtiyojga bog'liq.
2.4. Transport sozlash (Redis misol)
// Redis transport
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.REDIS,
options: { host: "localhost", port: 6379 },
});
// RabbitMQ transport
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.RMQ,
options: { urls: ["amqp://localhost:5672"], queue: "orders_queue" },
});Transport sozlash —
transport+options. Redis (host/port), RabbitMQ (urls/queue), Kafka (brokers). Faqat shu o'zgaradi — handler kodi bir xil 2.5-bob. Bu — NestJS'ning kuchi (transport-agnostik). Production'da Redis/RabbitMQ/Kafka (TCP emas).
Barcha transport'ning options maydonlari — bir joyda (har biri o'rnatilishi kerak bo'lgan paket bilan):
// 1) TCP — tug'ma (qo'shimcha paket yo'q). Eng oddiy — o'rganish uchun.
{ transport: Transport.TCP, options: { host: "0.0.0.0", port: 3001, retryAttempts: 5, retryDelay: 3000 } }
// 2) Redis — `npm i ioredis`. Yengil pub/sub 5.21-bob.
{ transport: Transport.REDIS, options: { host: "localhost", port: 6379, retryAttempts: 5, retryDelay: 3000 } }
// 3) NATS — `npm i nats`. Yengil, o'ta tez pub/sub (2.4a).
{ transport: Transport.NATS, options: { servers: ["nats://localhost:4222"], queue: "orders" } }
// 4) RabbitMQ — `npm i amqplib amqp-connection-manager`. Navbat, routing, dead-letter (2.4b).
{ transport: Transport.RMQ, options: { urls: ["amqp://localhost:5672"], queue: "orders_queue",
queueOptions: { durable: true }, noAck: false } }
// 5) Kafka — `npm i kafkajs`. Event streaming, durable, partition, replay (2.4c).
{ transport: Transport.KAFKA, options: { client: { brokers: ["localhost:9092"] },
consumer: { groupId: "orders-consumer" } } }
// 6) gRPC — `npm i @grpc/grpc-js @grpc/proto-loader`. Tez, tipli (protobuf — 2.4d).
{ transport: Transport.GRPC, options: { package: "orders", protoPath: join(__dirname, "orders.proto"),
url: "localhost:5000" } }Muhim: TCP tug'ma (paketsiz), boshqalari — mos drayver paketi kerak (
ioredis,nats,amqplib,kafkajs,@grpc/grpc-js).retryAttempts/retryDelay— klient ulanish uzilsa qayta urinish (TCP/Redis/NATS). RabbitMQ/Kafka'da ishonchlilik broker darajasida (navbat/offset).
2.4a. NATS transport (yengil, tez pub/sub)
// NATS mikroservis — yengil, past kechikish (low latency)
import { Transport } from "@nestjs/microservices";
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.NATS,
options: {
servers: ["nats://localhost:4222"],
queue: "orders_group", // queue group — bir guruhda faqat bitta instance oladi (load balance)
},
});
// Klient
ClientsModule.register([
{ name: "NATS_SERVICE", transport: Transport.NATS,
options: { servers: ["nats://localhost:4222"] } },
]);NATS — o'ta yengil, tez message tizimi (Go'da yozilgan broker). Queue group (
queue) — bir guruhda ko'p instance bo'lsa, har xabarni faqat bittasi oladi (yuk balansi — masshtab). Request-response (send) ham, pub/sub (emit) ham. Redis'ga o'xshash, lekin maxsus message uchun optimallashgan. Kichik-o'rta tizim uchun ideal (RabbitMQ'dan yengilroq).
2.4b. RabbitMQ transport (navbat — durable, ack)
// RabbitMQ — vazifa navbati (task queue), ishonchli yetkazish
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.RMQ,
options: {
urls: ["amqp://user:pass@localhost:5672"],
queue: "tasks_queue",
queueOptions: { durable: true }, // navbat restart'da saqlanadi (diskda)
noAck: false, // qo'lda tasdiq (ack) — ishonchli (yo'qolmaydi)
prefetchCount: 1, // bir vaqtda 1 vazifa (adolatli taqsimot)
},
});RabbitMQ — navbat asosli (message broker).
durable: true— navbat diskda (broker restart'da yo'qolmaydi).noAck: false— handler tugagachchannel.ackchaqiriladi (aks holda xabar qayta yuboriladi — ishonchli, at-least-once).prefetchCount— bir instance bir vaqtda nechta olishi (yuk balansi). Routing, dead-letter, retry — RabbitMQ kuchi (vazifa qayta ishlash — Misol 5).
2.4c. Kafka transport (event streaming — durable, replay)
// Kafka — event streaming, durable log, partition, replay
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.KAFKA,
options: {
client: {
clientId: "orders-service",
brokers: ["localhost:9092"],
},
consumer: {
groupId: "orders-consumer", // consumer group — partition'lar guruhga taqsimlanadi
},
run: { autoCommit: true },
},
});
// Kafka'da request-response — javob alohida topic'ga boradi (reply topic).
// Handler avtomatik `<pattern>.reply` topic'iga javob yozadi (NestJS boshqaradi).
@MessagePattern("order.get")
bitta(@Payload() message: any) {
return this.service.bitta(message.value.id); // message.value — Kafka payload
}Kafka — event streaming platformasi (durable, taqsimlangan log). Consumer group — partition'lar guruh a'zolariga taqsimlanadi (masshtab). Xabar saqlanadi (log — replay mumkin: eski voqealarni qayta o'qish, event sourcing).
message.value— Kafka'da payload konvert ichida (offset, partition, headers bilan). Og'ir infratuzilma — juda katta hajm, event sourcing, audit log uchun. Request-response NestJS'da reply-topic orqali (avtomatik).
2.4d. gRPC transport (tez, tipli — protobuf)
gRPC — Google'ning yuqori unumli RPC protokoli. Xizmat va message'lar .proto faylida (contract-first) e'lon qilinadi, so'ng binar (protobuf) formatda uzatiladi — JSON'dan tez va tipli. Xizmatlararo ichki RPC uchun eng samarali.
// orders.proto — kontrakt (xizmat + message tuzilishi)
syntax = "proto3";
package orders;
service OrdersService {
rpc FindOne (OrderById) returns (Order) {} // unary (so'rov-javob)
rpc FindMany (Empty) returns (stream Order) {} // server streaming (oqim)
}
message OrderById { int32 id = 1; }
message Empty {}
message Order {
int32 id = 1;
string title = 2;
double price = 3;
}// main.ts — gRPC mikroservis
import { join } from "path";
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.GRPC,
options: {
package: "orders", // .proto'dagi package nomi
protoPath: join(__dirname, "orders.proto"), // .proto fayl yo'li
url: "localhost:5000", // manzil
},
});
await app.listen();
// orders.controller.ts — @GrpcMethod (handler)
import { GrpcMethod } from "@nestjs/microservices";
import { Observable, Subject } from "rxjs";
@Controller()
export class OrdersController {
@GrpcMethod("OrdersService", "FindOne") // (service nomi, rpc metodi)
findOne(data: { id: number }): Order {
return this.service.bitta(data.id); // .proto'dagi Order qaytariladi
}
@GrpcMethod("OrdersService", "FindMany") // server streaming
findMany(): Observable<Order> {
const subject = new Subject<Order>();
this.service.hammasi().forEach((o) => subject.next(o));
subject.complete();
return subject.asObservable(); // oqim (stream) qaytariladi
}
}// Klient tomon — gRPC ClientProxy (proto'dan interfeys)
import { ClientGrpc, Transport } from "@nestjs/microservices";
import { OnModuleInit } from "@nestjs/common";
@Module({
imports: [
ClientsModule.register([
{ name: "ORDERS_PACKAGE", transport: Transport.GRPC,
options: { package: "orders", protoPath: join(__dirname, "orders.proto"), url: "localhost:5000" } },
]),
],
})
export class GatewayModule {}
@Injectable()
export class GatewayService implements OnModuleInit {
private ordersService: OrdersGrpcInterface;
constructor(@Inject("ORDERS_PACKAGE") private client: ClientGrpc) {}
onModuleInit() {
// proto'dagi service nomidan tipli klient olinadi
this.ordersService = this.client.getService<OrdersGrpcInterface>("OrdersService");
}
findOne(id: number): Observable<Order> {
return this.ordersService.findOne({ id }); // metod chaqiruvi (Observable qaytaradi)
}
}gRPC — protobuf (
.proto) bilan contract-first, tipli, binar (JSON'dan tez).@GrpcMethod("Service", "Method")— handler (route emas — proto metodi). KlientdaClientGrpc+getService()— proto'dan tipli interfeys (metod chaqiruvi). Streaming — unary (bitta javob) va server/client/bidirectional stream (streamkalit so'zi proto'da — Observable). Xizmatlararo tez, tipli RPC uchun eng yaxshi (ayniqsa ichki, polyglot tizim — proto til-agnostik).
2.4e. Serialization va Deserialization (xabar formatlash)
// Standart: NestJS avtomatik JSON serialize/deserialize qiladi.
// Maxsus format kerak bo'lsa — Serializer / Deserializer.
import { Serializer, OutgoingResponse } from "@nestjs/microservices";
export class CustomSerializer implements Serializer {
serialize(value: any): any {
// yuborishdan oldin (masalan konvert, sana ISO, maxsus format)
return { data: value, sentAt: new Date().toISOString() };
}
}
// Transport options'da ulash:
{
transport: Transport.TCP,
options: {
port: 3001,
serializer: new CustomSerializer(),
// deserializer: new CustomDeserializer(), // qabulda teskari
},
}Serialization — obyekt tarmoq orqali uzatiladigan formatga aylantirilishi (default: JSON). NestJS buni avtomatik qiladi — oddiy holatda hech narsa yozmaysiz. Lekin boshqa tizim bilan mos format (masalan, konvertli xabar, custom protokol) kerak bo'lsa —
Serializer(yuborishda) vaDeserializer(qabulda) interfeysini implement qilib, transportoptions'iga berasiz. gRPC/Kafka'da o'z formati bor (protobuf/Kafka message), shuning uchun bu asosan TCP/Redis/RMQ uchun.
2.4f. Xato, timeout va retry (ishonchlilik)
import { firstValueFrom, timeout, retry, catchError, throwError } from "rxjs";
async buyurtmaOl(id: number) {
return firstValueFrom(
this.client.send({ cmd: "order_get" }, id).pipe(
timeout(5000), // 5s ichida javob kelmasa — xato
retry({ count: 3, delay: 1000 }), // 3 marta qayta urinish (1s oraliq)
catchError((err) => // barchasi muvaffaqiyatsiz bo'lsa
throwError(() => new ServiceUnavailableException("Buyurtma xizmati javob bermadi")),
),
),
);
}Ishonchlilik — tarmoq — ishonchsiz (mikroservis realligi).
send— RxJS Observable, shuning uchun operatorlar bilan boyitiladi:timeout(ms)(javob kutish chegarasi — osilib qolmaslik),retry({ count, delay })(vaqtinchalik uzilishda qayta urinish),catchError(mahalliy fallback yoki HTTP exception'ga aylantirish). Transportoptions'idagiretryAttempts/retryDelay— bu ulanish darajasidagi retry (Observable'dagi — so'rov darajasidagi). Idempotent handler bilan retry xavfsiz (6-xato).
2.5. Message Pattern (so'rov-javob — request-response)
// Mikroservis (qabul qiluvchi) — handler
import { MessagePattern, Payload } from "@nestjs/microservices";
@Controller()
export class OrdersController {
@MessagePattern({ cmd: "buyurtma_yarat" }) // pattern (route kabi)
yarat(@Payload() data: CreateOrderDto) { // ma'lumot
return this.ordersService.yarat(data); // JAVOB qaytaradi
}
@MessagePattern({ cmd: "buyurtma_ol" })
bitta(@Payload() id: number) {
return this.ordersService.bitta(id);
}
}@MessagePattern — so'rov-javob (request-response): klient xabar yuboradi, javob kutadi (HTTP'ga o'xshash — controller route).
{ cmd: "..." }— pattern (mos handler).@Payload()— ma'lumot. Javob qaytaradi. Klient 2.7-bob bloklanadi (javobgacha kutadi). Sinxron aloqa uchun (ma'lumot kerak).
2.6. Event Pattern (fire-and-forget — voqea)
@EventPattern("buyurtma_yaratildi") // voqea (event)
async buyurtmaYaratildi(@Payload() data: any) {
await this.emailService.yubor(data.email); // email yuborish
// JAVOB QAYTARMAYDI (fire-and-forget)
}@EventPattern — voqea (fire-and-forget): klient voqea e'lon qiladi va kutmaydi (javobsiz — davom etadi). Bir voqeani ko'p xizmat tinglashi mumkin (buyurtma yaratildi email + SMS + statistika xizmatlari). Asinxron, bo'shashgan bog'lanish (loose coupling — 9). Voqea-asoslangan arxitektura asosi (event-driven).
2.7. ClientProxy (xizmatlararo aloqa)
// Klient (yuboruvchi xizmat) — ClientProxy
import { ClientProxy, ClientsModule, Transport } from "@nestjs/microservices";
@Module({
imports: [
ClientsModule.register([
{ name: "ORDERS_SERVICE", transport: Transport.TCP, options: { port: 3001 } },
]),
],
})
export class GatewayModule {}
@Injectable()
export class GatewayService {
constructor(@Inject("ORDERS_SERVICE") private client: ClientProxy) {}
buyurtmaYarat(dto: any) {
return this.client.send({ cmd: "buyurtma_yarat" }, dto); // so'rov-javob (Observable)
}
voqeaElon(data: any) {
this.client.emit("buyurtma_yaratildi", data); // voqea (fire-and-forget)
}
}ClientProxy — boshqa xizmatga xabar yuborish.
client.send(pattern, data)— so'rov-javob (Observable — javob kutadi — 2.5);client.emit(event, data)— voqea (kutmaydi — 2.6).ClientsModule.register(klient sozlash). Bu — xizmatlar orasidagi aloqa (gateway service). RxJS Observable 8.6-bob.
send— "cold" Observable:send()obyekt qaytaradi, lekin subscribe qilinmaguncha (yokifirstValueFrombilan) xabar aslida yuborilmaydi (Misol 8, 3-xato). Har subscribe — yangi so'rov (natijani qayta ishlatmoqchi bo'lsangiz —firstValueFrombilan bir marta oling).emitesa fire-and-forget — subscribe kutmaydi, lekin ishonchli yetkazish uchun ba'zi transportlar (RMQ/Kafka)emitnatijasini hamawaitqilishni qo'llab-quvvatlaydi (ulanish tayyorligini kutish). ClientProxy'ni yopish —onApplicationShutdown'daclient.close()(ulanishni tozalash — 8.15).
2.8. API Gateway (yagona kirish nuqtasi)
API GATEWAY naqsh:
┌─────────────┐
Mijoz │ API Gateway │ (HTTP — yagona kirish)
└──────┬──────┘
┌────────┼────────┐
▼ ▼ ▼
Users Orders Products (mikroservislar — TCP/Redis)
Service Service Service
(DB) (DB) (DB)
Gateway: auth, rate limit, routing, javoblarni birlashtirishAPI Gateway — mijoz uchun yagona kirish (HTTP). Mijoz har xizmatga to'g'ridan murojaat qilmaydi — Gateway'ga (u mikroservislarga yo'naltiradi — ClientProxy). Gateway: auth 8.9-bob, rate limit 2.13-bob, routing, javoblarni birlashtirish. Bu — mikroservis arxitekturasining standart naqshi (mijoz murakkablikni ko'rmaydi).
2.9. Hybrid application (HTTP + mikroservis)
// Bir ilova: HTTP + mikroservis (gateway)
async function bootstrap() {
const app = await NestFactory.create(AppModule); // HTTP
app.connectMicroservice({ // + mikroservis transport
transport: Transport.REDIS,
options: { host: "localhost", port: 6379 },
});
await app.startAllMicroservices(); // mikroservislarni boshlash
await app.listen(3000); // HTTP ham
}// Bir nechta transportni ulash mumkin (ko'p connectMicroservice)
const app = await NestFactory.create(AppModule);
app.connectMicroservice({ transport: Transport.REDIS, options: { host: "localhost", port: 6379 } });
app.connectMicroservice({ transport: Transport.RMQ, options: { urls: ["amqp://localhost:5672"], queue: "tasks" } });
// { inheritAppConfig: true } — global pipe/filter/interceptor'ni mikroservisga ham qo'llaydi
await app.startAllMicroservices(); // BARCHA ulangan transportlarni birga ishga tushiradi
await app.listen(3000); // HTTP ham tinglaydiHybrid app — bir NestJS ilova HTTP + mikroservis birga.
connectMicroservice(transport qo'shish — bir nechta chaqirilishi mumkin: Redis + RMQ birga) +startAllMicroservices(barchasini ishga tushirish).{ inheritAppConfig: true }— HTTP uchun sozlangan global pipe/guard/filter'ni mikroservis handlerlarga ham qo'llaydi (8.5, 8.6). Gateway shunday (tashqi HTTP + ichki message). Bir DI konteyner, modul daraxti. Gateway uchun ideal (public API + internal RPC).
2.10. Taqsimlangan tranzaksiya (Saga — qiyin muammo)
Muammo: mikroservislarda umumiy DB yo'q (har xizmat o'z DB'si)
bir tranzaksiya bir necha xizmatni qamraydi (buyurtma + to'lov + zaxira)
oddiy DB transaction 8.13-bob ishlamaydi (taqsimlangan!)
Yechim — SAGA naqshi:
- Har qadam alohida (buyurtma to'lov zaxira)
- Xato bo'lsa — KOMPENSATSIYA (orqaga: to'lov qaytarish, buyurtma bekor)
- Choreography (event) yoki Orchestration (markaziy)Taqsimlangan tranzaksiya (mikroservisning eng qiyin muammosi): har xizmat o'z DB'si oddiy ACID transaction 8.13-bob ishlamaydi. Saga naqshi: ketma-ket qadamlar + xato bo'lsa kompensatsiya (orqaga qaytarish). Choreography (event — 2.6) yoki orchestration (markaziy boshqaruvchi). Bu — mikroservisning murakkabligi (monolitda yo'q — 2.1). 9-QISM'da chuqur.
2.11. Exception va validatsiya (mikroservis)
import { RpcException } from "@nestjs/microservices";
@MessagePattern({ cmd: "buyurtma_ol" })
async bitta(@Payload() id: number) {
const order = await this.service.bitta(id);
if (!order) throw new RpcException("Buyurtma topilmadi"); // RPC xato (8.6 filter)
return order;
}
// Gateway'da RpcException HTTP exception'ga aylantiriladi// RPC exception filter — mikroservis tomonda (xatoni formatlab qaytarish)
import { Catch, RpcExceptionFilter, ArgumentsHost } from "@nestjs/common";
import { RpcException } from "@nestjs/microservices";
import { Observable, throwError } from "rxjs";
@Catch(RpcException)
export class RpcErrorFilter implements RpcExceptionFilter<RpcException> {
catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
return throwError(() => exception.getError()); // klientga xato Observable orqali qaytadi
}
}
// Klient (Gateway) tomonda RPC xatoni HTTP'ga aylantirish:
async bitta(id: number) {
return firstValueFrom(
this.client.send({ cmd: "buyurtma_ol" }, id).pipe(
catchError((err) => { // RpcException'dan kelgan xato
throw new NotFoundException(err.message ?? err); // HTTP 404
}),
),
);
}Mikroservis exception —
RpcException(HTTP exception emas — 8.1). Mikroservis tomonda xato Observable orqali klientga qaytadi (HTTP status yo'q — transport RPC). Gateway'da RPC xato HTTP'ga aylantiriladi (catchError+ HTTP exception, yokiRpcExceptionFilter— 8.6). Validatsiya (ValidationPipe— 8.5) ham ishlaydi (message payload validatsiya qilinadi — noto'g'ri payload RpcException). Pipe/guard/interceptor/filter — mikroservisda ham ishlaydi (8.5, 8.6). Bir xil NestJS imkoniyatlar, faqat kontekst RPC (host.switchToRpc()).
2.12. Service discovery va aloqa (kontekst)
Mikroservislar bir-birini qanday topadi?
- Statik (config — kichik tizim — port/host)
- Service registry (Consul, Eureka — dinamik)
- Kubernetes service (10 — DNS, load balancing)
- Message broker (RabbitMQ/Kafka — broker orqali — discovery kerak emas)
Broker (RabbitMQ/Kafka) — eng oson (xizmatlar brokerga ulanadi, bir-birini bilmaydi)Service discovery — xizmatlar bir-birini topishi. Statik (config — 8.14), service registry (Consul), Kubernetes (10 — DNS), yoki message broker (RabbitMQ/Kafka — xizmatlar brokerga ulanadi, bir-birini to'g'ridan bilmaydi — eng oson). Broker yondashuvi — bo'shashgan bog'lanish (9). DevOps (10) bilan bog'liq.
2.13. Rate limiting va Throttler (Gateway — 14)
import { ThrottlerModule, ThrottlerGuard } from "@nestjs/throttler";
@Module({
imports: [
ThrottlerModule.forRoot([{ ttl: 60000, limit: 100 }]), // 1 daqiqada 100
],
providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }], // global (8.6)
})
export class GatewayModule {}
// Yoki endpoint
@Throttle({ default: { limit: 5, ttl: 60000 } }) // login (brute-force — 8.9)
@Post("login")
login() {}Throttler (rate limiting — 14, 5.20) — Gateway'da so'rov sonini cheklash (DoS, brute-force himoyasi).
ThrottlerModule+ThrottlerGuard(global — 8.6).@Throttle(endpoint — login — 8.9). Redis bilan (taqsimlangan — ko'p instance — 8.15). Gateway — auth + rate limit + routing 2.8-bob.
2.14. Best practices (mikroservis)
Monolit'dan boshlang (kerak bo'lganda ajrating — "monolith first" — 2.1)
To'g'ri transport (Redis/RabbitMQ/Kafka — ehtiyojga — 2.3)
Message (so'rov-javob) vs Event (fire-and-forget — to'g'ri tanlov — 2.5, 2.6)
API Gateway (yagona kirish — auth/rate limit — 2.8)
Event-driven (bo'shashgan bog'lanish — 2.6, 9)
Saga (taqsimlangan tranzaksiya — 2.10); idempotent handler
Har xizmat o'z DB'si (mustaqillik); RpcException 2.11-bob
Monitoring/tracing (taqsimlangan — NestJS 11 trace ID — 2.12, 10)3. Sintaksis — tez ma'lumotnoma
// Mikroservis 2.2-bob: createMicroservice(AppModule, { transport, options })
// Handler (2.5, 2.6)
@MessagePattern({ cmd: "x" }) yarat(@Payload() data) {} // so'rov-javob
@EventPattern("voqea") handle(@Payload() data) {} // voqea
// Klient (2.7)
ClientsModule.register([{ name: "SVC", transport, options }])
@Inject("SVC") client: ClientProxy
client.send(pattern, data) // so'rov-javob (Observable)
client.emit(event, data) // voqea
// gRPC (2.4d)
@GrpcMethod("OrdersService", "FindOne") findOne(data) {} // proto metodi
this.client.getService<T>("OrdersService") // klient tipli interfeys
// Hybrid 2.9-bob: app.connectMicroservice(); app.startAllMicroservices()
// Ishonchlilik (2.4f): .pipe(timeout(5000), retry({ count: 3, delay: 1000 }), catchError(...))
// Serializer (2.4e): options.serializer / options.deserializer (maxsus format)4. Batafsil kod namunalari
Misol 1 — Orders mikroservis (2.2, 2.5)
// orders/main.ts
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(OrdersModule, {
transport: Transport.REDIS,
options: { host: "localhost", port: 6379 },
});
app.useGlobalPipes(new ValidationPipe({ whitelist: true })); // (8.5)
await app.listen();
console.log("Orders mikroservis ishlayapti");
}
bootstrap();
// orders/orders.controller.ts
@Controller()
export class OrdersController {
constructor(private ordersService: OrdersService) {}
@MessagePattern({ cmd: "order_create" }) // so'rov-javob
yarat(@Payload() dto: CreateOrderDto) {
return this.ordersService.yarat(dto);
}
@MessagePattern({ cmd: "order_get" })
bitta(@Payload() id: number) {
return this.ordersService.bitta(id);
}
@EventPattern("payment_completed") // voqea (to'lov xizmatidan)
async tolovTugadi(@Payload() data: { orderId: number }) {
await this.ordersService.holatYangila(data.orderId, "to'langan");
}
}Misol 2 — API Gateway (2.7, 2.8, 2.9)
// gateway/gateway.module.ts
@Module({
imports: [
ClientsModule.register([
{ name: "ORDERS_SERVICE", transport: Transport.REDIS, options: { host: "localhost", port: 6379 } },
{ name: "USERS_SERVICE", transport: Transport.REDIS, options: { host: "localhost", port: 6379 } },
]),
],
controllers: [GatewayController],
})
export class GatewayModule {}
// gateway/gateway.controller.ts (HTTP — mijoz uchun)
@Controller("orders")
export class GatewayController {
constructor(
@Inject("ORDERS_SERVICE") private ordersClient: ClientProxy,
@Inject("USERS_SERVICE") private usersClient: ClientProxy,
) {}
@Post()
@UseGuards(JwtAuthGuard) // auth 8.9-bob — Gateway'da
yarat(@Body() dto: CreateOrderDto) {
return this.ordersClient.send({ cmd: "order_create" }, dto); // mikroservisga
}
@Get(":id")
async bitta(@Param("id") id: number) {
// Bir necha xizmatdan birlashtirish (2.8)
const order = await firstValueFrom(this.ordersClient.send({ cmd: "order_get" }, id));
const user = await firstValueFrom(this.usersClient.send({ cmd: "user_get" }, order.userId));
return { ...order, user }; // birlashtirilgan javob
}
}Misol 3 — Event-driven (voqea — 2.6)
// Buyurtma yaratilganda — ko'p xizmat tinglaydi (loose coupling)
// Gateway/Orders — voqea e'lon
@Injectable()
export class OrdersService {
constructor(@Inject("EVENT_BUS") private eventBus: ClientProxy) {}
async yarat(dto: CreateOrderDto) {
const order = await this.repo.save(dto);
this.eventBus.emit("order_created", { // voqea (fire-and-forget — 2.6)
orderId: order.id, userId: order.userId, email: order.email,
});
return order; // kutmaydi
}
}
// Email mikroservis — tinglaydi
@EventPattern("order_created")
async emailYubor(@Payload() data: any) {
await this.mailService.buyurtmaTasdiq(data.email, data.orderId);
}
// SMS mikroservis — ham tinglaydi (mustaqil)
@EventPattern("order_created")
async smsYubor(@Payload() data: any) {
await this.smsService.yubor(data.userId);
}
// bir voqea, ko'p tinglovchi (Orders ularni bilmaydi — bo'shashgan)Misol 4 — Saga (taqsimlangan tranzaksiya — 2.10)
// Orchestration saga (markaziy boshqaruvchi)
@Injectable()
export class OrderSaga {
constructor(
@Inject("PAYMENT") private payment: ClientProxy,
@Inject("INVENTORY") private inventory: ClientProxy,
) {}
async buyurtmaYarat(dto: CreateOrderDto) {
// 1. Zaxira band qilish
const zaxira = await firstValueFrom(this.inventory.send({ cmd: "reserve" }, dto.items));
try {
// 2. To'lov
const tolov = await firstValueFrom(this.payment.send({ cmd: "charge" }, dto));
return { success: true, tolov };
} catch (e) {
// KOMPENSATSIYA — zaxirani qaytarish (2.10)
await firstValueFrom(this.inventory.send({ cmd: "release" }, zaxira.id));
throw new BadRequestException("To'lov amalga oshmadi, buyurtma bekor qilindi");
}
}
}Misol 5 — RabbitMQ (navbat — vazifa qayta ishlash)
// RabbitMQ mikroservis (vazifa navbati)
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.RMQ,
options: {
urls: ["amqp://localhost:5672"],
queue: "tasks_queue",
queueOptions: { durable: true }, // navbat saqlanadi (restart'da)
noAck: false, // qo'lda tasdiq (ishonchli)
},
});
@EventPattern("process_video")
async videoQaytaIshla(@Payload() data: any, @Ctx() context: RmqContext) {
const channel = context.getChannelRef();
const message = context.getMessage();
try {
await this.videoService.qaytaIshla(data);
channel.ack(message); // muvaffaqiyat tasdiq
} catch (e) {
channel.nack(message); // xato qayta (yoki dead-letter)
}
}Misol 6 — Throttler (Gateway rate limit — 2.13)
// gateway.module.ts
@Module({
imports: [
ThrottlerModule.forRootAsync({
inject: [ConfigService],
useFactory: () => ({
throttlers: [{ ttl: 60000, limit: 100 }], // umumiy: 1 daqiqada 100
// storage: Redis (taqsimlangan — 8.15)
}),
}),
],
providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
})
export class GatewayModule {}
// Maxsus limit (login — brute-force — 8.9)
@Throttle({ default: { limit: 5, ttl: 60000 } })
@Post("auth/login")
login(@Body() dto: LoginDto) {}Misol 7 — Mikroservis tuzilishi (monorepo)
Mikroservis loyiha (monorepo — NestJS workspace):
apps/
├── gateway/ (HTTP — API Gateway, auth, routing — Misol 2)
├── users/ (foydalanuvchi xizmati — o'z DB)
├── orders/ (buyurtma xizmati — o'z DB — Misol 1)
├── payment/ (to'lov xizmati)
└── notification/ (email/SMS — event tinglovchi — Misol 3)
libs/
└── common/ (umumiy DTO, interface, konstanta)
Har xizmat mustaqil (o'z DB, deploy); libs — umumiy kod
nest generate app <nom> (monorepo)Misol 8 — Async send va xato boshqaruv (2.11)
import { firstValueFrom, timeout, catchError } from "rxjs";
@Injectable()
export class GatewayService {
constructor(@Inject("ORDERS") private client: ClientProxy) {}
async buyurtmaOl(id: number) {
try {
return await firstValueFrom(
this.client.send({ cmd: "order_get" }, id).pipe(
timeout(5000), // 5 soniya kutish (timeout)
catchError((e) => { throw new ServiceUnavailableException("Xizmat javob bermadi"); }),
),
);
} catch (e) {
throw e;
}
}
}Misol 9 — Health check (mikroservis — 2.12, 8.15)
// Har mikroservis — health (monitoring — 10)
@Controller()
export class HealthController {
@MessagePattern({ cmd: "health" })
health() {
return { status: "ok", service: "orders", time: new Date().toISOString() };
}
}
// Gateway — barcha xizmat health (aggregation)
@Get("health")
async health() {
const services = ["ORDERS", "USERS", "PAYMENT"];
// har biriga health so'rovi birlashtirilgan holat
}Misol 10 — Monolit mikroservis o'tish (2.1)
O'tish strategiyasi (monolith first — 2.1):
1. Monolit (modulli — 8.1) — toza modul chegaralari
2. Og'ir/mustaqil modulni aniqlash (masalan: notification, video)
3. Shu modulni alohida xizmat qilish (strangler fig naqsh)
4. Event/message bilan ulash 2.6-bob
5. Asta-sekin boshqalarni (kerak bo'lsa)
Hammasi birdan emas — bosqichma-bosqich (xavf kam)
Toza modul 8.1-bob — keyin oson ajratiladi5. To'g'ri va noto'g'ri holatlar
1) Erta mikroservis (kichik loyiha)
kichik loyiha mikroservis (keraksiz murakkablik — 2.1)
monolit (modulli) kerak bo'lganda ajrating2) So'rov-javob hammasiga (event kerak joyda)
email'ni send (kutadi — sekin)
emit (voqea — fire-and-forget — 2.6)3) Mikroservisda umumiy DB
bir DB (bog'lanish — mustaqillik yo'q)
har xizmat o'z DB'si (2.1)4) Taqsimlangan tranzaksiya oddiy transaction bilan
DB transaction (taqsimlanmaydi — 2.10)
Saga (kompensatsiya)5) Mijoz to'g'ridan xizmatlarga
mijoz har xizmatga (murakkab, xavfsizlik)
API Gateway (yagona kirish — 2.8)6. Keng tarqalgan xatolar va yechimlari
Xato 1 — Mikroservis ulanmaydi
Sababi: transport/port mos emas, broker ishlamayapti 2.4-bob. Yechimi: transport bir xil; Redis/RabbitMQ ishga tushir.
Xato 2 — send javob bermaydi (osilib qoladi)
Sababi: pattern mos emas, yoki handler yo'q 2.5-bob. Yechimi: pattern aniq mos; timeout qo'shing (Misol 8).
Xato 3 — Observable subscribe bo'lmaydi
Sababi: send Observable (RxJS) — await emas. Yechimi: firstValueFrom (Misol 8).
Xato 4 — Event yo'qoladi
Sababi: durable emas, yoki tinglovchi yo'q edi 2.6-bob. Yechimi: durable queue (RabbitMQ); Kafka (replay).
Xato 5 — Taqsimlangan ma'lumot nomutanosib
Sababi: Saga/kompensatsiya yo'q 2.10-bob. Yechimi: Saga; idempotent; event sourcing.
Xato 6 — Bir voqea ikki marta
Sababi: at-least-once delivery. Yechimi: idempotent handler (ikki marta = bir marta).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Arxitektura (9): monolit vs mikroservis, Saga, event-driven.
- DevOps (10): Docker, Kubernetes, service discovery, deploy.
- Redis 8.15-bob: transport, throttler.
- Auth 8.9-bob: Gateway auth.
- Event 8.6-bob: EventPattern, RxJS.
- DB 8.13-bob: har xizmat o'z DB.
- Throttler 5.20-bob: rate limit.
- Navbat 8.22-bob: RabbitMQ/BullMQ.
- Monitoring 8.15-bob: health, tracing.
8. Eng yaxshi amaliyotlar (best practices)
- Monolit'dan boshlang ("monolith first" — kerak bo'lganda ajrating — 2.1).
- To'g'ri transport (Redis/RabbitMQ/Kafka — ehtiyojga — 2.3).
- Message vs Event (so'rov-javob vs fire-and-forget — 2.5, 2.6).
- API Gateway (yagona kirish — auth/rate limit — 2.8).
- Event-driven (bo'shashgan bog'lanish — 2.6, 9).
- Saga (taqsimlangan tranzaksiya — 2.10); idempotent handler.
- Har xizmat o'z DB'si (mustaqillik — 2.1).
- RpcException (mikroservis xato — 2.11); timeout (Misol 8).
- Throttler (rate limit — Gateway — 2.13).
- Monitoring/tracing (taqsimlangan — 2.12, 10).
9. Amaliy loyiha: "Mikroservis E-commerce Tizimi"
NestJS mikroservislarni mustahkamlash.
Maqsad
E-commerce'ni mikroservislarga ajratish: Gateway + Users + Orders + Payment + Notification — message/event bilan.
Talablar (requirements)
- Mikroservislar: Orders, Users (Redis transport — Misol 1, 2.2).
- Message pattern: CRUD (so'rov-javob — Misol 1, 2.5).
- Event pattern: order_created email/SMS (Misol 3, 2.6).
- API Gateway: HTTP + ClientProxy + auth (Misol 2, 2.8).
- Hybrid: Gateway HTTP + mikroservis 2.9-bob.
- Saga: buyurtma + to'lov + zaxira + kompensatsiya (Misol 4, 2.10).
- RabbitMQ: vazifa navbati (video/email — Misol 5).
- Throttler: Gateway rate limit (Misol 6, 2.13).
- Xato + timeout: RpcException, firstValueFrom + timeout (Misol 8, 2.11).
- Health: har xizmat (Misol 9, 2.12).
Maslahatlar (hint)
- Transport bir xil (2.4, 1-xato).
- send firstValueFrom (2.7, 3-xato).
- Email emit (event — 2.6, 2-holat).
- Saga kompensatsiya (2.10, 4-holat).
- Gateway yagona kirish (2.8, 5-holat).
- Idempotent handler (6-xato).
"Tayyor" mezonlari (acceptance criteria)
- Mikroservislar (transport).
- Message pattern (CRUD).
- Event pattern (ko'p tinglovchi).
- API Gateway (ClientProxy, auth).
- Hybrid app.
- Saga (kompensatsiya).
- RabbitMQ navbat.
- Throttler.
- Xato + timeout.
- Health check.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda NestJS mikroservislarni chuqur o'rgandik:
- Monolit vs mikroservis (qachon qaysi — "monolith first" — 2.1); NestJS mikroservis 2.2-bob; transport'lar (TCP/Redis/NATS/RabbitMQ/Kafka/gRPC — 2.3, 2.4; har biri config — 2.4a–2.4d).
- Message pattern (so'rov-javob — 2.5); Event pattern (fire-and-forget — 2.6); ClientProxy (xizmatlararo,
sendcold Observable — 2.7); serialization (2.4e); timeout/retry (2.4f). - gRPC (proto +
@GrpcMethod+ streaming — 2.4d); NATS (yengil pub/sub — 2.4a); Kafka (event streaming, replay — 2.4c). - API Gateway (yagona kirish — 2.8); hybrid app 2.9-bob; Saga (taqsimlangan tranzaksiya — 2.10); RpcException 2.11-bob.
- Service discovery 2.12-bob; Throttler (rate limit — 2.13); best practices 2.14-bob.
Keyingi bob — 8.17-bob: GraphQL (NestJS GraphQL). Mikroservisni bildik; endi REST'ga muqobil API uslubini — GraphQL — o'rganamiz: code-first vs schema-first, resolver (@Query/@Mutation), @ObjectType, subscriptions (real-time), va N+1 muammosi (DataLoader). GraphQL — mijoz aniq kerakli ma'lumotni so'raydigan moslashuvchan API.
Foydalanilgan rasmiy/ishonchli manbalar
- docs.nestjs.com/microservices/basics (transport, @MessagePattern, @EventPattern, ClientProxy)
- docs.nestjs.com/microservices/redis · docs.nestjs.com/microservices/rabbitmq · docs.nestjs.com/microservices/kafka
- docs.nestjs.com/microservices/nats · docs.nestjs.com/microservices/grpc (proto, @GrpcMethod)
- docs.nestjs.com/microservices/exception-filters · docs.nestjs.com/faq/hybrid-application
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!