NestJS 微服务架构概览
一、单体 vs 微服务
单体应用:所有模块打包在一个进程中部署,简单但扩展困难。
微服务:将系统拆分为多个独立进程,每个服务有自己的数据库,通过网络通信。
单体应用:
┌─────────────────────────────┐
│ UserModule PostModule │
│ AuthModule EmailModule │ → 一个进程,一个数据库
└─────────────────────────────┘
微服务:
┌───────────┐ ┌───────────┐ ┌───────────┐
│ user-svc │ │ post-svc │ │ email-svc │ → 各自进程、各自数据库
└───────────┘ └───────────┘ └───────────┘
↑ ↑ ↑
└───────── 消息队列/RPC ────────┘| 维度 | 单体 | 微服务 |
|---|---|---|
| 复杂度 | 低 | 高(网络、数据一致性) |
| 独立部署 | 不可以 | 可以(每个服务独立发布) |
| 独立扩展 | 不可以 | 可以(按需水平扩展) |
| 故障隔离 | 差(一处挂全挂) | 好(服务间隔离) |
| 技术选型 | 统一 | 各自选择最适合的栈 |
| 适合场景 | 中小型应用、快速迭代 | 大型系统、高并发、多团队 |
二、NestJS 微服务核心抽象
NestJS 将传输层抽象为可互换的适配器——业务代码不关心底层通信协议:
typescript
// ── 服务端:启动纯微服务进程 ────────────────────
// user-service/src/main.ts
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.REDIS,
options: {
host: process.env.REDIS_HOST ?? 'localhost',
port: parseInt(process.env.REDIS_PORT ?? '6379'),
},
});
await app.listen();
console.log('user-service is listening');
}
bootstrap();typescript
// ── 客户端:在 Gateway 中连接微服务 ─────────────
// gateway/src/app.module.ts
@Module({
imports: [
ClientsModule.registerAsync([
{
name: 'USER_SERVICE',
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
transport: Transport.REDIS,
options: {
host: config.get('REDIS_HOST'),
port: config.get('REDIS_PORT'),
},
}),
},
]),
],
})
export class GatewayModule {}三、传输层对比与选型
| 传输层 | 包 | 适用场景 | 特点 |
|---|---|---|---|
| TCP | 内置 | 内部服务同步调用 | 简单直连,无持久化 |
| Redis | ioredis | 轻量异步通信 | Pub/Sub,简单配置 |
| RabbitMQ | amqplib | 企业消息队列 | 可靠投递、死信队列、路由规则 |
| Kafka | kafkajs | 高吞吐事件流 | 分区、消费组、日志持久化 |
| gRPC | @grpc/grpc-js | 高性能内部 RPC | 强类型(Protobuf)、双向流 |
| NATS | nats | 轻量级云原生 | 低延迟,Kubernetes 友好 |
选型建议:
- 开发/演示:Redis(零配置)
- 生产关键业务:RabbitMQ(可靠投递,消息确认)
- 日志/大数据管道:Kafka(高吞吐,持久化)
- 服务间同步调用:gRPC(高性能,类型安全)
四、混合应用(HTTP + 微服务)
API Gateway 通常既暴露 HTTP 接口(对外),又作为微服务客户端(对内):
typescript
// gateway/src/main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 同时启动一个微服务监听器(接收来自其他服务的消息)
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.REDIS,
options: { host: 'localhost', port: 6379 },
});
await app.startAllMicroservices(); // 先启动微服务监听
await app.listen(3000); // 再启动 HTTP 服务器
console.log('Gateway: HTTP on :3000 + microservice on Redis');
}五、服务发现与健康检查
bash
npm install @nestjs/terminustypescript
// health/health.controller.ts
import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator, MicroserviceHealthIndicator } from '@nestjs/terminus';
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
private microservice: MicroserviceHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
// 数据库健康
() => this.db.pingCheck('database'),
// 依赖的微服务健康
() => this.microservice.pingCheck<TcpClientOptions>('user-service', {
transport: Transport.TCP,
options: { host: 'user-service', port: 3001 },
}),
]);
}
}六、本仓库实战项目结构
practice/05-microservices/
├── docker-compose.yml # 统一编排所有服务
├── gateway/ # API Gateway(HTTP :3000)
│ ├── src/
│ │ ├── users/ # 代理到 user-service
│ │ ├── posts/ # 代理到 content-service
│ │ └── auth/ # JWT 验证(在 Gateway 层)
│ └── Dockerfile
├── user-service/ # 用户服务(微服务,监听 Redis)
│ ├── src/
│ │ └── users/
│ └── Dockerfile
└── content-service/ # 内容服务(微服务,监听 Redis)
├── src/
│ └── posts/
└── Dockerfileyaml
# docker-compose.yml
version: '3.8'
services:
redis:
image: redis:7-alpine
ports: ['6379:6379']
user-service:
build: ./user-service
environment:
REDIS_HOST: redis
DATABASE_URL: postgres://...
depends_on: [redis, postgres]
content-service:
build: ./content-service
environment:
REDIS_HOST: redis
depends_on: [redis]
gateway:
build: ./gateway
ports: ['3000:3000']
environment:
REDIS_HOST: redis
JWT_SECRET: ${JWT_SECRET}
depends_on: [redis, user-service, content-service]七、微服务拆分原则
- 按业务能力拆分(不按技术层):
user-service、order-service而非dao-service - 每个服务拥有自己的数据库:数据隔离是微服务的核心约束
- 服务大小适中:团队能够完全理解和维护("两个披萨"原则)
- 先做好单体,再拆分:过早拆分是反模式,分布式系统复杂度极高
- 定义清晰的服务契约:消息格式(Schema)应独立维护,避免耦合
常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
| 微服务连接失败但没有错误日志 | TCP/Redis 传输层连接超时静默失败 | 监听 microservice.connect() 的异常,或加连接超时配置 |
| 消息发送后没有响应(请求超时) | @MessagePattern 未在目标服务注册 | 确认消息名称一致,检查目标微服务是否正常启动 |
| 混合应用 HTTP 和微服务端口冲突 | connectMicroservice 与 listen 端口相同 | 微服务用独立端口,或使用不需要端口的传输层(如 Redis) |