前言
在软件开发的世界里,微服务架构是一个经常被提及的概念。但它到底是什么意思呢?为什么现在这么多团队和公司选择使用微服务?而NestJS又是如何帮助开发者构建微服务的?
什么是微服务
想象一下,你想建一座大厦。你可以选择由一间强大的公司全权负责,从地基到尖塔的每一部分;另一种方法是,你选择多间专业的小公司,一家负责电梯,一家负责供电系统等等。每间公司都是建筑部分的专家,并负责自己区域内的一切。微服务就是采用这后者的思路,但对象是软件应用。
简单来说,微服务是一种将单一应用程序分解为一组小服务的架构风格,每个服务运行在自己的进程中,并使用轻量级的机制通常是HTTP RESTful API进行通信。每个服务都围绕着特定的业务能力构建,并且可以独立地部署到生产环境。它们可以用不同的编程语言编写,并使用不同的数据存储技术。
为什么需要微服务
当你的应用程序变得越来越大,就会越来越难以维护和扩展,这种现象被称为“代码的大泥球”。微服务架构适应这种情况的原因有几个:
- 独立性:微服务使得团队可以独立地更新、部署和扩展各个组件。
- 易于理解和维护:每个服务都相对较小,负责单一的功能,因此更容易理解和修改。
- 技术多样性:不同的服务可以采用最适合其业务需求的技术栈。
- 弹性:服务之间相互独立,一个服务的故障不会直接影响到其他服务。
- 可扩展性:可以独立扩展某个特定服务,而不必扩展整个应用。
NestJS微服务实战
NestJS是一个用于构建高效,可靠的服务端应用程序的框架。它完美地支持微服务架构模型。NestJS提供了与微服务相关的模块和装饰器,这些工具允许你轻松创建以消息传递机制(比如通过使用TCP、RabbitMQ或Kafka)进行交互的独立服务。
接下来创建一个简单的微服务系统,其中包括一个主服务和一个微服务,它们通过NestJS的微服务模块通信。
一、创建主服务
首先,我们创建一个新的NestJS应用作为主服务:
nest new main-service
二、创建微服务
然后再创建一个新的应用作为微服务:
nest new microservice-app
三、设置微服务通讯
在微服务应用中,更新 main.ts
文件来初始化微服务:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
async function bootstrap(){
const app = await NestFactory.create(AppModule);
const microservice = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: {
host: '127.0.0.1',
port: 8877
}
});
await app.startAllMicroservicesAsync();
await app.listen(3000);
console.log(`Microservice is running`);
}
bootstrap();
在这段代码中,我们为微服务指定了 Transport.TCP
传输机制,并设置它的监听地址和端口。同时,我们调用了 startAllMicroservicesAsync
启动所有的微服务,它会启动TCP服务器并开始监听。
四、在微服务中创建服务处理器
接下来,我们在微服务中创建一个服务处理器。在 app.service.ts
文件中,你可能会写类似下面的代码:
import { Injectable } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
@Injectable()
export class AppService {
@MessagePattern('get_data')
getData(data: any): string {
// 实际业务逻辑
return `Data received: ${JSON.stringify(data)}`;
}
}
在上面的 AppService
类中,我们使用 MessagePattern
装饰器定义了一个消息处理器,该处理器将响应名为 get_data
的消息并返回一些处理过的数据。
五、在主服务中调用微服务
现在,我们需要在主服务中调用微服务。这需要我们设置主服务中的微服务客户端,并通过该客户端发送消息。
更新主服务的 app.module.ts
来配置微服务客户端:
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
ClientsModule.register([
{
name: 'MICROSERVICE_APP',
transport: Transport.TCP,
options: {
host: '127.0.0.1',
port: 8877
},
},
]),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
在主服务的 app.controller.ts
中定义一个调用微服务的方法:
import { Controller, Get } from '@nestjs/common';
import { ClientProxy, ClientProxyFactory, Transport } from '@nestjs/microservices';
import { AppService } from './app.service';
@Controller()
export class AppController {
private client: ClientProxy;
constructor(private readonly appService: AppService) {
this.client = ClientProxyFactory.create({
transport: Transport.TCP,
options: {
host: '127.0.0.1',
port: 8877,
},
});
}
@Get()
async getData() {
const response = await this.client.send('get_data', { id: 1 }).toPromise();
return response;
}
}
在这个例子中,我们创建了一个名为 client
的 ClientProxy
对象,并使用TCP传输和确定的主机及端口对其进行了配置。通过 client.send('get_data', { id: 1 })
,我们就可以发送消息 get_data
并附带一些数据。使用 toPromise()
来处理异步响应。
六、测试微服务系统
一切都设置好之后,你就可以分别运行主服务和微服务了。
# 在两个不同的终端中
# 启动主服务
npm run start
# 启动微服务
npm run start
当主服务和微服务都启动之后,你可以通过浏览器访问主服务的根路径,它会通过微服务客户端向微服务发送请求,微服务会处理这个请求并返回结果。
总结
通过以上步骤,我们展示了如何使用NestJS创建微服务架构的一个简单例子。微服务架构允许我们将应用分解为独立可部署的模块,每个模块专注于一个小的业务范围,提高了开发的可维护性和可扩展性。NestJS的微服务模块简化了服务间的通信和管理,使开发者能够专注于业务逻辑的实现,而不是底层的通信细节。
值得注意的是,微服务的设计和管理需要谨慎考虑,它可能带来一些挑战,比如服务间通信的复杂性、数据一致性、分布式系统的测试和部署等。因此,在选择微服务架构前,你应该评估它是否适合你的项目和团队。