NestJS在单独的进程中运行工作程序。

18
我正在实现NestJS worker,queues,使用Bull
根据文档,工作者和服务器(将)在同一“进程”中运行,但我想在单独的进程中运行工作者,以便阻塞主事件循环。
我认为这被称为“在单独的二进制文件中运行任务”或其他什么。
无论如何,我尝试通过谷歌搜索它,在NestJS的文档中找到类似的内容,但没有找到。
++换句话说:
我有一个主项目(我的当前项目),我想在单独的进程(独立应用程序)中创建工作者,并希望连接我的当前主项目和工作者。但我确实在文档中找不到。
在哪个模块中应该实例化Bull的实例?我假设我会在我的主模块中保留我的producer,并在我的工作者模块中保留consumer

我该怎么做?

请注意,“分离进程”并不是指在Bull的文档中定义的运行特定任务的单独进程。我想将整个worker模块部署在单独的进程中,或者使用应该使用的术语。

++ [如有可能,额外说明]

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


你可以将你的工作程序单独放在一个应用中,我认为这就是你在此上下文中所说的“单独二进制文件”的意思。 - Isolated
“我在Bull的文档中找不到任何东西...”,你确定吗? https://github.com/OptimalBits/bull#separate-processes 这个呢? - Isolated
请检查我的答案,它与示例显示的内容相同,只是示例不是最好的。 - Isolated
2个回答

23

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

这在此处有记录。 "单独的二进制文件"也不是问题。二进制文件是编译的产品,Node.js没有编译,因此您需要一个单独的应用程序。

您不需要任何解决方法,这实际上就是 Bull 和可选的 Nest.js 的本质。

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

术语

我认为术语存在一些混淆,因此在本文章中请假设:

  1. process 是您的 application 在其中运行的内容(如果您查看操作系统进程管理器,它应该是 node)。
  2. application 是运行在单独的 process 中的一个 Node.js 项目。
  3. worker 是仅专注于处理队列任务的 application
  4. QueueJob 是 Bull 的术语。
  5. ProcessorProcess 是 Nest.js 的 @nestjs/bull 的术语。

解决方案

以下是如何创建一个分别运行在独立进程中的 applicationworker。按照以下说明后,您应该会在操作系统进程管理器中看到两个进程在运行。

创建一个新的 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 {}
src目录中创建一个名为app.processor.ts的新文件:

Create a new file: app.processor.ts in src directory:


// 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”方面,您就完成了。现在您需要做的是在您的“应用程序”(主项目)中更新您的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

是的,我看到了这个,但是不知道如何在我的项目中使用它。 我有一个主项目(当前的项目),我想在单独的进程(独立应用程序)中创建工作进程,并希望连接我的当前主项目和工作进程。但我在文档中找不到相关内容。 - Daksh Gargas
我应该在哪个模块中实例化我的 Bull 实例?我假设我会将producer放在我的主模块中,而将consumer放在我的工作模块中。 - Daksh Gargas
@DakshGargas 我更新了我的回答。你不应该有单独的模块,而应该有两个独立的项目/应用程序。看一下,如果需要进一步帮助,请告诉我,但希望这样就可以解决问题了。 - Isolated
我正在做同样的事情,但这不会将我的工作程序作为单独的“应用程序”生成。但也许我在某个步骤上出了问题...当你让我执行nest new my-worker时...这是一个全新的项目吗?如果是这样,我该如何将它与我的当前项目连接起来?此外,我的“消费者”将使用我当前项目中的多个模块。 - Daksh Gargas
@DarshGargas 是的,这是一个全新的项目。但问题是你需要通过 API 或其他方式来使用这些模块,或者将它们从主项目复制到你的 worker 中。然而,采用这种方法可以获得更清晰的分离和独立进程。你不必担心连接两者,因为这可以通过 Redis 实现,只需将两个项目连接到相同的 Redis 即可。 - Isolated
嘿,我添加了一个解决方案,你可能想要检查一下。 感谢你的帮助...这是一个不错的起点。还有,谢谢你澄清我的疑惑并提供详细的定义 :) - Daksh Gargas

13
虽然 Isolated提供的答案应该可以工作,但我不想运行一个全新的项目并按照他建议的导入我的模块。所以经过更多的研究和开发,我找到了一种(更好的)方法来实现这个目标。
就像我们的“父”项目有一个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命令。
"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"

为了启动我的工作程序,我会在另一个(终端)shell中运行以下命令...

开发者

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

或者

产品

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

附言:你不一定需要添加dotenv/config

额外奖励:

如果你想在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

嗨!您对这种方式满意吗?使用Bull / Redis和您的worker? 您尝试过运行多个应用程序实例和worker吗? - Armel Larcier
嘿,到目前为止它完成了工作...但是还没有尝试运行多个 runner 实例。我想我只需要在另一个端口上生成它,就应该没问题了。我已经这样在 Staging 上运行我的 worker 15 天了,到目前为止没有任何投诉! - Daksh Gargas
你已经同时运行了多个应用程序吗? - Armel Larcier
“应用程序的倍数?”我不明白…… - Daksh Gargas
非常感谢你的帮助,@DakshGargas! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接