Nest.js 中创建微服务架构实践

1,344 阅读3分钟

「这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

介绍

微服务架构是Nest.js支持的功能之一,通过将功能分解到各个离散的服务中以实现对解决方案的解耦。这篇文章通过一个例子来为大家展示创建微服务架构的步骤。

开始

首先我们创建一个nestjs-microservices文件夹,在文件夹里创建三个应用

$ mkdir nestjs-microservices && cd nestjs-microservices
$ npm install -g @nestjs/cli
$ nest new users
$ nest new profiles
$ nest new bff

然后分别为这三个应用安装微服务模块 npm i @nestjs/microservices --save

我们现在需要开发一个获取用户的接口,获取到的数据包含用户id、用户名和用户其他属性,而用户id和用户名会放在项目users中,而用户属性会存放在项目profiles中,bff项目用来存放主接口,当我们请求接口的时候会在bff中获取用户列表和用户属性列表,然后根据用户id进行合并,返回给用户。

微服务

Nest 内置了几种不同的微服务传输层实现,默认是 TCP 协议,我们今天选择REDIS作为消息中转 项目usersprofiles 是我们的两个微服务,我们打开这两个项目的main.ts

// users/src/main.ts 和 profiles/src/main.ts
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.REDIS,
      options: {
        url: 'redis://localhost:6379',
      },
    },
  );
  app.listen(() => console.log('Services started'));
}
bootstrap();

接下来我们分别为usersprofiles 添加逻辑代码。先修改usersapp.controller.ts

// users/src/app.controller.ts
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';

import { Profile } from './app.interface';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @MessagePattern({ cmd: 'get_profiles' })
  getProfiles(): Profile[] {
    return this.appService.getProfiles();
  }
}

/users/src/app.service.ts

// users/src/app.service.ts
import { Injectable } from '@nestjs/common';
import { User } from './app.interface';

@Injectable()
export class AppService {
  users = [
    { id: '1', name: '张三' },
    { id: '2', name: '李四' },
  ];

  getUsers(): User[] {
    return this.users;
  }
}

然后打开profilesapp.controller.ts

// profiles/src/app.controller.ts
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';

import { Profile } from './app.interface';
import { AppService } from './app.service';

@Controller()
export class AppController {
 constructor(private readonly appService: AppService) {}

 @MessagePattern({ cmd: 'get_profiles' })
 getProfiles(): Profile[] {
   return this.appService.getProfiles();
 }
}

/profiles/src/app.service.ts

// profiles/src/app.service.ts
import { Injectable } from '@nestjs/common';

import { Profile } from './app.interface';

@Injectable()
export class AppService {
  profiles = [
    { id: '1', hobby: '打游戏', age: 21 },
    { id: '2', hobby: '听音乐', age: 18 },
  ];

  getProfiles(): Profile[] {
    return this.profiles;
  }
}

这样我们就完成了微服务的修改,接下来我们修改bbf的项目 /bff/src/app.controller.ts

import { Controller, Get, Inject } from '@nestjs/common';
import { ClientProxy } from '@nestjs/microservices';

import { Account, Profile, User } from './app.interface';

@Controller()
export class AppController {
  constructor(
    @Inject('PUBSUB')
    private readonly client: ClientProxy,
  ) {}

  @Get('accounts')
  async getAccounts(): Promise<Account[]> {
    const users = await this.client
      .send<User[]>({ cmd: 'get_users' }, { page: 1, items: 10 })
      .toPromise();
    const profiles = await this.client
      .send<Profile[]>({ cmd: 'get_profiles' }, { ids: users.map((u) => u.id) })
      .toPromise();

    return users.map<Account>((u) => ({
      ...u,
      ...profiles.find((p) => p.id === u.id),
    }));
  }
}

我们在这里面的操作是,利用ClientProxy的send的方法与其他两个微服务通信,获取到各自的数据,然后把它们根据 id 进行合并,返回我们完整的用户数据。

接下来最后一步我们需要在 /bff/src/app.module.ts 里面进行注册和注入token PUBSUB 这个需要和其他两个应用一致。

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: 'PUBSUB',
        transport: Transport.REDIS,
        options: {
          url: 'redis://localhost:6379',
        },
      },
    ]),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

这样就完成了一个最简单的微服务架构

测试

全部的应用都启动后,我们通过 postMan 调用接口 http://localhost:3000/accounts

image.png

可以看到,我们已经获取到了合并后的完整数据。