import { existsSync } from 'node:fs'; import { resolve } from 'node:path'; import { config as loadDotenvFile } from 'dotenv'; import Redis from 'ioredis'; import pino from 'pino'; import { loadWorkerConfig } from './config'; function loadDotenv(): void { for (const file of ['.env.local', '.env']) { const path = resolve(process.cwd(), file); if (existsSync(path)) { loadDotenvFile({ path }); } } } async function main(): Promise { loadDotenv(); const config = loadWorkerConfig(); const logger = pino({ level: config.logLevel, base: { service: 'lingxi-worker' } }); const redis = new Redis(config.redisUrl, { lazyConnect: true, maxRetriesPerRequest: null, enableOfflineQueue: false, }); redis.on('error', (error: Error) => { logger.error({ err: error }, 'redis connection error'); }); await redis.connect(); await redis.ping(); logger.info({ queue: config.queue, concurrency: config.concurrency }, 'worker 已启动,等待任务'); // A0 只验证进程与依赖可用;BullMQ 消费者在 M1 批次接入(QUEUE 环境变量决定队列) const heartbeat = setInterval(() => { logger.debug({ queue: config.queue }, 'worker heartbeat'); }, 30_000); heartbeat.unref(); const shutdown = async (signal: string): Promise => { clearInterval(heartbeat); logger.info({ signal }, 'worker 正在退出'); await redis.quit(); }; process.on('SIGINT', () => { void shutdown('SIGINT'); }); process.on('SIGTERM', () => { void shutdown('SIGTERM'); }); } void main().catch((error: unknown) => { process.stderr.write( `Worker 启动失败:${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`, ); process.exitCode = 1; });