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 中假设:
- A
process
是你的 application
在里面运行的(如果你查看你的 OS 进程管理器,它应该是 node
)。
application
是 一个 Node.js 项目,在单独的 process
. 中运行
- A
worker
是一个 application
,它 仅专注于处理队列作业。
Queue
和 Job
是 Bull 的术语。
Processor
和 Process
是 Nest.js @nestjs/bull
的术语
解决方案
以下是如何在单独的进程中创建一个 应用程序 和一个 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.ts
在 src
目录:
// 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 上创建一个存储库,我会带你走上正轨。
参考
虽然 应该可以工作,但我真的不想 运行 一个全新的项目并按照他建议的方式导入我的模块。所以经过更多的研发,我想出了一个(更好的)方法。
就像我们的“父”项目有我们的 index.ts
或 main.ts
文件一样,在同一个目录(不一定是)中,创建一个 worker.ts
和worker.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
我正在实施 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 中假设:
- A
process
是你的application
在里面运行的(如果你查看你的 OS 进程管理器,它应该是node
)。 application
是 一个 Node.js 项目,在单独的process
. 中运行
- A
worker
是一个application
,它 仅专注于处理队列作业。 Queue
和Job
是 Bull 的术语。Processor
和Process
是 Nest.js@nestjs/bull
的术语
解决方案
以下是如何在单独的进程中创建一个 应用程序 和一个 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.ts
在 src
目录:
// 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 上创建一个存储库,我会带你走上正轨。
参考
虽然
就像我们的“父”项目有我们的 index.ts
或 main.ts
文件一样,在同一个目录(不一定是)中,创建一个 worker.ts
和worker.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