NestJS 运行 worker 在一个单独的进程中

NestJS run worker in a separate process

我正在实施 NestJS worker,queues, using Bull

根据 documentation,工作人员和服务器(将)运行 在同一个“进程”中,但我想 运行 工作人员在一个单独的进程中,以便阻塞主事件循环。

我认为它叫做“运行在单独的二进制文件中执行任务”或其他名称。

无论如何,我尝试用谷歌搜索它,浏览了 NestJS 的文档,但找不到类似的东西。

++ 换句话说:

我有一个主项目(我当前的项目),我想在单独的进程(独立应用程序)中创建工作人员,并希望连接我当前的主项目和工作人员。而且我真的无法在文档中找到它。

我应该在哪个模块中实例化我的 Bull 实例?我假设我会在主模块中保留 producer,在工作模块中保留 consumer

我该怎么做?

请注意,我所说的“单独流程”并不是指 运行 在单独的流程中执行特定任务,如 Bull 的 documentation 中所定义。 我想将整个工作模块部署在一个单独的进程中,或者应该使用任何术语。

++ [额外的,如果可能的话]

在运行连接我的服务器和worker之前,我还想检查我的worker(bull实例)是否成功连接到我的Redis服务器。我在 Bull 的文档中找不到任何内容...您认为有好的解决方法吗?

可以使用该文档来实现整个工作器。如果您在独立模式下使用Nest.js,您可以只拥有处理器和进程。

这已记录在案 here。 “Separate binary”也不是问题。二进制文件是编译的产物,Node.js 未编译,因此您需要一个单独的 应用程序 .

你不需要任何解决方法,这就是 Bull 的本质,并且可以选择 Nest.js。

有时您需要调整文档中的示例以满足您的需要,这可能需要一些时间来学习。

术语

我认为术语存在一些混淆,因此在此 post 中假设:

  1. A process 是你的 application 在里面运行的(如果你查看你的 OS 进程管理器,它应该是 node)。
  2. application一个 Node.js 项目,在单独的 process.
  3. 中运行
  4. A worker 是一个 application,它 仅专注于处理队列作业
  5. QueueJob 是 Bull 的术语。
  6. ProcessorProcess 是 Nest.js @nestjs/bull
  7. 的术语

解决方案

以下是如何在单独的进程中创建一个 应用程序 和一个 worker 运行。按照这些说明操作后,您应该会看到两个进程 运行 您的进程管理器。

创建一个新的 Nest.js 应用程序,我们将用于您的 worker:

nest new my-worker

打开 src/main.ts 并将 bootstrap 函数中的所有内容替换为:

const app = await NestFactory.createApplicationContext(AppModule);

安装 Bull 和 Nest.js 实现:

yarn add @nestjs/bull bull

打开 src/app.module.ts 并从 controllers 中删除 AppController,然后将 BullModule.registerQueue 添加到导入(来自 @nestjs/bull.

您的 src/app.module.ts 现在应该看起来像:

// app.module.ts
// ... imports
@Module({
  imports: [
    BullModule.registerQueue({
      name: 'my-queue',
      redis: {
        host: 'localhost',
        port: 6379,
      },
    }),
  ],
})
export class AppModule {}

创建新文件:app.processor.tssrc 目录:

// app.processor.ts
// ... imports
@Processor('my-queue')
export class AppConsumer {
    @Process('namedjob')
    async processNamedJob(job: Job<any>): Promise<any> {
        // do something with job and job.data
    }
}

您已经完成了 worker 方面的工作。现在你需要做的就是在你的application(主项目)中,更新你的AppModule以包含BullModule.registerQueue(如上)并注入它:

export class MyService {
  constructor(@InjectQueue('my-queue') private queue: Queue) {}
}

然后使用this.queue.add('namedJob', data);

试试上面的方法,如果遇到困难,请在 Github 上创建一个存储库,我会带你走上正轨。

参考

  1. https://github.com/OptimalBits/bull#separate-processes
  2. https://docs.nestjs.com/standalone-applications

虽然 应该可以工作,但我真的不想 运行 一个全新的项目并按照他建议的方式导入我的模块。所以经过更多的研发,我想出了一个(更好的)方法。

就像我们的“父”项目有我们的 index.tsmain.ts 文件一样,在同一个目录(不一定是)中,创建一个 worker.tsworker.module.ts.

worker.module.ts 中,确保您再次注册 Bull 模块 [BullModule.forRoot({})] 并包含您的消费者需要的所有进口商品。

providers 中,您应该添加我们的 consumers 就可以了。

worker.ts 看起来像这样(没什么特别的):

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
import { WorkerModule } from './worker/worker.module';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(WorkerModule);
  app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER));
  process.env.WORKER_HTTP_PORT = process.env.WORKER_HTTP_PORT ?? '4001';
  await app.listen(process.env.WORKER_HTTP_PORT);
  console.debug(`Worker is running on ${await app.getUrl()}`);
}
bootstrap();

你的 nest-cli.json 应该喜欢这样的东西

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "entryFile": "main",
  "compilerOptions": {
    "assets": ["**/*.graphql"],
    "watchAssets": true
  }
}

并创建一个新的 nest-cli-worker.json

{
    "collection": "@nestjs/schematics",
    "sourceRoot": "src",
    "entryFile": "worker",
    "compilerOptions": {
        "watchAssets": true
    }
}

现在,问题是,如何运行呢?

我正在使用 yarn 命令启动我的服务器(在 package.json 中定义它们)

为了start我的server,我

"start:dev": "yarn nest start --watch -e 'node -r dotenv/config -r source-map-support/register'"

"start:prod": "node -r dotenv/config -r ./tsconfig-paths-bootstrap.js dist/src/main.js"

start 我的 worker,我会 运行 以下命令(s) 在 另一个 (终端)shell...

Dev

"worker:start:dev": "yarn nest start --config nest-cli-worker.json --watch -e 'node -r dotenv/config -r source-map-support/register'"

Prod

"worker:start:prod": "node -r dotenv/config -r ./tsconfig-paths-bootstrap.js dist/src/worker.js"

P.S不一定要加dotenv/config.

奖金:

If you want to run your server(s) in docker

这是我的 docker-compose.yaml 文件

version: '3.8'

services:
  main:
    container_name: my-server
    image: xxx.amazonaws.com/xx/xxx:${CONTAINER_IMAGE_TAG:-latest}
    ports:
      - 80:80
    command: node -r dotenv/config -r ./tsconfig-paths-bootstrap.js dist/src/main.js #My `prod` command for main server
    volumes:
      - xxx
    links:
      - xxx
    environment:
      xxx
    # .env is generated by Elastic Beanstalk, don't provide one
    env_file:
      - .env
  worker:
    container_name: worker-server #YOUR WORER
    image: xxx.us-west-2.amazonaws.com/xxx:${CONTAINER_IMAGE_TAG:-latest}
    ports:
      - 90:90
    links:
      - xxx
    command: node -r dotenv/config -r ./tsconfig-paths-bootstrap.js dist/src/worker.js  #prod command for Worker
    volumes:
      - xxx
    environment:
      xxx
    # .env is generated by Elastic Beanstalk, don't provide one
    env_file:
      - .env