NestJS 11 + Fastify 统一错误体与 trace_id,/health 真实探活 DB/Redis,OpenAPI 3.1 契约含 20 个错误码;shared 包提供错误码、权限点、ULID、分页与通用 schema;worker 完成队列进程骨架。
63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
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<void> {
|
|
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<void> => {
|
|
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;
|
|
});
|