diff --git a/lib/server/start.js b/lib/server/start.js index e6d9e6f..555c73c 100644 --- a/lib/server/start.js +++ b/lib/server/start.js @@ -402,15 +402,64 @@ async function startup(path, connector, omitWatchers, omitTimers, routine) { if (!omitTimers) { appLoader.startTimers(); } - process.on('SIGINT', async () => { - await appLoader.unmount(); - process.exit(0); - }); + let isShutingdown = false; const shutdown = async () => { - await httpServer.close(); - await koa.removeAllListeners(); - await appLoader.unmount(); - (0, polyfill_1.removePolyfill)("startup"); + if (isShutingdown) { + return; + } + isShutingdown = true; + console.log('服务器正在关闭中...'); + try { + await httpServer.close(); + await koa.removeAllListeners(); + await appLoader.execStopRoutines(); + await appLoader.unmount(); + (0, polyfill_1.removePolyfill)("startup"); + } + catch (err) { + console.error('关闭服务器时出错:', err); + } }; + // 处理优雅关闭的统一入口 + let shutdownStarted = false; + const handleShutdown = async (signal) => { + // 防止重复处理 + if (shutdownStarted) { + console.warn('关闭流程已启动,忽略此信号'); + return; + } + shutdownStarted = true; + console.log(`\n收到 ${signal} 信号,准备关闭服务器...`); + // 移除所有信号处理器,防止重复触发 + process.removeAllListeners('SIGINT'); + process.removeAllListeners('SIGTERM'); + process.removeAllListeners('SIGQUIT'); + process.removeAllListeners('SIGHUP'); + try { + await shutdown(); + process.exit(0); + } + catch (err) { + console.error('关闭过程出错:', err); + process.exit(1); + } + }; + // 监听终止信号进行优雅关闭 + // SIGINT - Ctrl+C 发送的中断信号 + process.on('SIGINT', () => { + handleShutdown('SIGINT'); + }); + // SIGTERM - 系统/容器管理器发送的终止信号(Docker、K8s、PM2等) + process.on('SIGTERM', () => { + handleShutdown('SIGTERM'); + }); + // SIGQUIT - Ctrl+\ 发送的退出信号 + process.on('SIGQUIT', () => { + handleShutdown('SIGQUIT'); + }); + // SIGHUP - 终端关闭时发送(可选) + process.on('SIGHUP', () => { + handleShutdown('SIGHUP'); + }); return shutdown; } diff --git a/lib/server/watch.js b/lib/server/watch.js index 8019e88..c801260 100644 --- a/lib/server/watch.js +++ b/lib/server/watch.js @@ -499,6 +499,7 @@ const watch = async (projectPath, config) => { resolveFullPaths: true, }); function treatFile(filePath) { + console.log(`Processing file for alias replacement: ${filePath}`); const fileContents = fs_1.default.readFileSync(filePath, 'utf8'); const newContents = runFile({ fileContents, filePath }); // do stuff with newContents diff --git a/lib/template.js b/lib/template.js index cb0e8e3..b1d2bfc 100644 --- a/lib/template.js +++ b/lib/template.js @@ -424,7 +424,7 @@ function tsConfigPathsJsonContent(deps) { compilerOptions: { baseUrl: "./", paths, - typeRoots: ["./typings"] + typeRoots: ["./typings", "node_modules/@types"] } }, null, '\t'); } diff --git a/package.json b/package.json index 602c8ba..c6a87c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@xuchangzju/oak-cli", - "version": "4.0.29", + "version": "4.0.30", "description": "client for oak framework", "main": "lib/index.js", "scripts": { @@ -44,7 +44,7 @@ "babel-plugin-module-resolver": "^5.0.0", "events": "^3.3.0", "fork-ts-checker-webpack-plugin": "^8.0.0", - "ts-node": "^10.9.1", + "ts-node": "^10.9.2", "typescript": "^5.2.2" }, "dependencies": { @@ -112,8 +112,8 @@ "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.5.3", "node-watch": "^0.7.4", - "oak-backend-base": "^4.1.24", - "oak-domain": "^5.1.30", + "oak-backend-base": "^4.1.25", + "oak-domain": "^5.1.31", "oak-frontend-base": "^5.3.43", "parse-asn1": "5.1.6", "postcss": "^8.4.4", diff --git a/src/server/start.ts b/src/server/start.ts index 598ba37..4cb3992 100644 --- a/src/server/start.ts +++ b/src/server/start.ts @@ -492,18 +492,71 @@ export async function startup { - await appLoader.unmount(); - process.exit(0); + let isShutingdown = false; + const shutdown = async () => { + if (isShutingdown) { + return; + } + isShutingdown = true; + console.log('服务器正在关闭中...'); + try { + await httpServer.close(); + await koa.removeAllListeners(); + await appLoader.execStopRoutines(); + await appLoader.unmount(); + removePolyfill("startup"); + } catch (err) { + console.error('关闭服务器时出错:', err); + } + } + + // 处理优雅关闭的统一入口 + let shutdownStarted = false; + const handleShutdown = async (signal: string) => { + // 防止重复处理 + if (shutdownStarted) { + console.warn('关闭流程已启动,忽略此信号'); + return; + } + shutdownStarted = true; + + console.log(`\n收到 ${signal} 信号,准备关闭服务器...`); + + // 移除所有信号处理器,防止重复触发 + process.removeAllListeners('SIGINT'); + process.removeAllListeners('SIGTERM'); + process.removeAllListeners('SIGQUIT'); + process.removeAllListeners('SIGHUP'); + + try { + await shutdown(); + process.exit(0); + } catch (err) { + console.error('关闭过程出错:', err); + process.exit(1); + } + }; + + // 监听终止信号进行优雅关闭 + // SIGINT - Ctrl+C 发送的中断信号 + process.on('SIGINT', () => { + handleShutdown('SIGINT'); }); - const shutdown = async () => { - await httpServer.close(); - await koa.removeAllListeners(); - await appLoader.unmount(); + // SIGTERM - 系统/容器管理器发送的终止信号(Docker、K8s、PM2等) + process.on('SIGTERM', () => { + handleShutdown('SIGTERM'); + }); - removePolyfill("startup"); - } + // SIGQUIT - Ctrl+\ 发送的退出信号 + process.on('SIGQUIT', () => { + handleShutdown('SIGQUIT'); + }); + + // SIGHUP - 终端关闭时发送(可选) + process.on('SIGHUP', () => { + handleShutdown('SIGHUP'); + }); return shutdown } \ No newline at end of file diff --git a/template/src/routines/stop.ts b/template/src/routines/stop.ts new file mode 100644 index 0000000..7e3ec01 --- /dev/null +++ b/template/src/routines/stop.ts @@ -0,0 +1,26 @@ +import { Routine } from 'oak-domain/lib/types/Timer'; +import { EntityDict } from '@oak-app-domain'; +import { BackendRuntimeContext } from '../context/BackendRuntimeContext'; + +// process.on('uncaughtException', (err) => { +// console.error(`Caught exception: ${err}`); +// // Optionally, you can exit the process or perform cleanup +// }); + +// process.on('unhandledRejection', (err) => { +// console.error(`Caught rejection: ${err}`); +// // Optionally, you can exit the process or perform cleanup +// }); + + +const stopRoutines: Array> = [ + { + name: '示例性routine_stop', + routine: async (context, env) => { + console.log('示例性routine执行,请在src/routine/stop.ts中关闭'); + return context.opResult; + }, + }, +]; + +export default stopRoutines;