Nestjs microservice practice - dynamic microservice creation link

All microservices require service governance

Service governance includes (configuration center, service discovery, registration service, etc.), and common ones include Java's Nacos. This article does not focus on service governance. It only explains how to use the nest gateway and dynamically implement microservice injection at the gateway layer.

The case on the nestjs official website obviously favors manual registration of microservices, for example:

/** Model */
@Module({
    
    
  imports: [
 	/** Model 中使用并注册 */ 
    ClientsModule.register([
      {
    
     name: 'MATH_SERVICE', transport: Transport.TCP },
    ]),
  ]
  ...
});

/** service / controller 使用 */
constructor(
  /** 通过 @Inject 装饰器只能挨个进行倒入,有多少服务倒入多少 */
  @Inject('MATH_SERVICE') private client: ClientProxy,
) {
    
    }

The above examples listed on the official website are definitely not enough for real microservice development, because the services are not dynamically changed according to the configuration of the configuration center. In this case, if a certain service takes up a lot of CPU and memory, it will cause The machine is down and cannot be dynamically expanded. I can only restart the service and manually change the configuration. What I need is completely dynamic. Each service deployment is deployed independently. For high-CPU services, dynamically expand the server and automatically switch IP addresses. Port lines.

First of all, do not register all services in the Model. It is recommended to use them all in service/controller. I will take controller as an example:

import {
    
     All, Controller, Req, Res } from '@nestjs/common';
import {
    
     ClientProxyFactory, Transport } from '@nestjs/microservices';
import {
    
     createProxyServer } from 'http-proxy';

/** 动态数组,这里后面要通过定时任务去配置中心拉取 */
const serviceList = [
  {
    
    
    name: 'user-center',
    host: '127.0.0.1',
    port: 65531,
  },
];

@Controller()
export class AppController {
    
    
  /** 首先所有路由不管任何请求方式全部代理到这个方法上 */
  @All('*')
  async root(@Req() req, @Res() res): Promise<any> {
    
    
    /** 获取所有可获取的参数 */
    const {
    
     method, originalUrl, query, body, headers } = req;

    /** 根据 url 字符串切割一下获取,主要获取第一级和第二级路由 */
    const list = originalUrl.split('?')[0].split('/').filter((item: string) => item !== '');

    /** 所有,开头为 /api 的参数,并且路由大于并且等于 2 级以上的,基本都可以调用微服务 */
    if (list.length >= 2 && list[0] === 'api') {
    
    
    
      /** 微服务真正的地址,是三级路径 */
      const path = `/${
      
      list.slice(2).join('/')}`;

      /** 服务名称是二级目录 */
      const service = list[1];

      /** 查找服务名称是否存在 */
      const server = serviceList.find((item) => item.name === service);

      /** 服务存在 */
      if (server) {
    
    
      
        /** 错误执行 */
        const errorNext = (err: string) => {
    
    
          /** 地址错误 */
          if (err === 'There is no matching message handler defined in the remote service.') {
    
    
            /** 手动关闭服务链接 */
            this[service].close();
            /** 清楚链接,下次链接如果服务可用就触发重新链接 */
            this[service] = undefined;
            
            res.status(404);
            res.send({
    
    
              code: -1,
              msg: '地址错误',
            });
            
          } else {
    
    
            console.log(`其他错误: ${
      
      err}`);
          }
        };
        /** 当前 class 中找不到服务,需要创建服务 */
        if (!this[service]) {
    
    
          /** 动态创建服务链接 */
          this[service] = ClientProxyFactory.create({
    
    
            transport: Transport.TCP,
            options: {
    
    
              host: server.host,
              port: server.port,
            },
          });
          try {
    
    
            /** 链接服务 */
            await this[service].connect();
            /** 发送消息*/
            const data = this[service].send(
              /** 服务的接口地址是 { method = 请求方法, path = 等于相对路径 }  */
              {
    
     method, path },
              /** 把所有 http 的参数全部传递过去,让那边服务自己判断处理 */
              {
    
     query, body, headers },
            );
            /** 接受流消息 */
            data.subscribe({
    
    
              /** 成功执行 next */
              next: (_res_: any) => {
    
    
                res.status(200);
                res.send(_res_);
              },
              /** 失败吧错误信息丢个 errorNext */
              error: (err: any) => errorNext(err),
            });
          } /** 服务存在,但是可能荡机了,又或者服务正在重启过程中 */ catch (err) {
    
    
            /** 手动关闭服务链接 */
            this[service].close();
            /** 清楚链接,下次链接如果服务可用就触发重新链接 */
            this[service] = undefined;
            res.status(503);
            res.send({
    
    
              code: -1,
              msg: '当前服务正在维护中',
            });
          }
        } /** 服务已存在 */ else {
    
    
          /** 发送消息*/
          const data = this[service].send(
            /** 服务的接口地址是 { method = 请求方法, path = 等于相对路径 }  */
            {
    
     method, path },
            /** 把所有 http 的参数全部传递过去,让那边服务自己判断处理 */
            {
    
     query, body, headers },
          );
          /** 接受流消息 */
          data.subscribe({
    
    
            /** 成功执行 next */
            next: (_res_: any) => {
    
    
              res.status(200);
              res.send(_res_);
            },
            /** 失败吧错误信息丢个 errorNext */
            error: (err: any) => errorNext(err),
          });
        }
      } /** 未找到服务 */ else {
    
    
        res.status(404);
        res.send({
    
    
          code: -1,
          msg: '未找到对应的服务',
        });
      }
    } /** 反向代理到 admin 地址 */ else if (list.length >= 2 && list[0] === 'admin') {
    
    
      const proxy = createProxyServer({
    
    
        target: 'http://127.0.0.1:4000',
        changeOrigin: true,
      });
      proxy.on('error', () => {
    
    
        res.status(503);
        res.send({
    
    
          code: -1,
          msg: '当前服务正在维护中',
        });
      });
      proxy.web(req, res);
    } /** 反向代理到 nuxt ssr 前端页面地址 */ else {
    
    
      const proxy = createProxyServer({
    
    
        target: 'http://127.0.0.1:5000',
        changeOrigin: true,
      });
      proxy.on('error', () => {
    
    
        res.status(503);
        res.send({
    
    
          code: -1,
          msg: '当前服务正在维护中',
        });
      });
      proxy.web(req, res);
    }
  }
}

The above is my core logic part. The main logic should be the following steps:

/** 1、创建服务 */
const proxy = ClientProxyFactory.create({
    
    
  transport: Transport.TCP,
  options: {
    
    
    host: '127.0.0.1',
    port: 65531,
  },
});

/** 2、服务连接 */
await proxy.connect();

/** 3、发送消息 */
const data = this[service].send(
  消息接口,
  附加数据,
);

/** 4、接受流消息 */
data.subscribe({
    
    
  /** 成功消息 */
  next: (_res_: any) => {
    
    
    res.status(200);
    res.send(_res_);
  },
  /** 排错 */
  error: (err: any) => {
    
    },
});

おすすめ

転載: blog.csdn.net/weixin_43704471/article/details/135403752