如何让BullMQ Worker的JS文件在Next.js后端自动运行?
Next.js中BullMQ Worker实例化文件的执行方案
除了在API文件中导入的方式,还有以下几种实用方案:
1. 自定义服务器入口文件(App Router推荐)
如果使用Next.js App Router,可以在项目根目录创建server.ts(或server.js)作为自定义服务器入口,在其中直接导入Worker文件,服务器启动时就会自动初始化Worker。
示例代码:
// server.ts import { createServer } from 'http'; import { parse } from 'url'; import next from 'next'; // 导入Worker文件,触发实例化 import './lib/workers/email-worker'; const dev = process.env.NODE_ENV !== 'production'; const app = next({ dev }); const handle = app.getRequestHandler(); app.prepare().then(() => { createServer((req, res) => { const parsedUrl = parse(req.url!, true); handle(req, res, parsedUrl); }).listen(process.env.PORT || 3000, () => { console.log(`> Ready on http://localhost:${process.env.PORT || 3000}`); }); });
修改package.json的启动脚本:
{ "scripts": { "dev": "ts-node server.ts", "build": "next build", "start": "ts-node server.ts" } }
2. 在Middleware中初始化(按需触发)
利用Next.js的Middleware全局拦截特性,在首次请求进入时动态导入Worker文件,确保Worker初始化。注意加单例判断,避免重复执行初始化逻辑。
示例代码:
// middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; let workerInitialized = false; export function middleware(request: NextRequest) { if (!workerInitialized) { // 动态导入Worker文件,仅执行一次 import('./lib/workers/email-worker'); workerInitialized = true; console.log('Email worker initialized via middleware'); } return NextResponse.next(); } // 配置Middleware生效范围,比如覆盖所有路由 export const config = { matcher: '/:path*', };
3. 独立进程运行Worker(任务密集场景推荐)
将Worker作为独立进程与Next.js服务器分开运行,避免服务器重启影响Worker运行,也便于单独监控和扩容。
步骤:
- 创建单独的Worker启动脚本:
// scripts/start-email-worker.js const { Worker } = require('bullmq'); const redisConfiguration = require('../lib/config/redis'); const emailWorkerJob = require('../lib/jobs/email-job'); const emailWorker = new Worker("emailSchedule", emailWorkerJob, { connection: redisConfiguration }); emailWorker.on('ready', () => { console.log('Email worker started successfully'); }); emailWorker.on('error', (err) => { console.error('Email worker error:', err); });
- 安装
concurrently工具用于同时启动多个进程:
npm install concurrently --save-dev
- 修改
package.json启动脚本:
{ "scripts": { "dev": "concurrently \"next dev\" \"npm run start-worker\"", "start-worker": "node scripts/start-email-worker.js", "build": "next build", "start": "concurrently \"next start\" \"npm run start-worker\"" } }
4. 在Server Component/getServerSideProps中初始化(不推荐,依赖页面渲染)
- Pages Router:在页面的
getServerSideProps中导入Worker,用全局变量判断避免重复初始化:
// pages/index.tsx export async function getServerSideProps() { if (!global.emailWorker) { const { emailWorker } = await import('../lib/workers/email-worker'); global.emailWorker = emailWorker; } return { props: {} }; }
- App Router:在根Layout Server Component中直接导入Worker文件,服务器渲染时会执行初始化:
// app/layout.tsx import './lib/workers/email-worker'; export default function RootLayout({ children }) { return ( <html lang="en"> <body>{children}</body> </html> ); }
这种方式依赖页面或布局的访问,可靠性不如前几种方案,仅适合小型项目临时使用。
内容的提问来源于stack exchange,提问作者Nicholas Wong
相关产品推荐
相关产品推荐

