imean-service-engine 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mod.cjs +118 -9
- package/dist/mod.d.cts +55 -4
- package/dist/mod.d.ts +55 -4
- package/dist/mod.js +118 -9
- package/package.json +1 -1
package/dist/mod.cjs
CHANGED
@@ -779,10 +779,11 @@ var Microservice = class {
|
|
779
779
|
lease;
|
780
780
|
scheduler;
|
781
781
|
abortController;
|
782
|
-
isShuttingDown = false;
|
783
782
|
statisticsTimer;
|
784
783
|
wsHandler;
|
785
784
|
actionHandlers = /* @__PURE__ */ new Map();
|
785
|
+
activeRequests = /* @__PURE__ */ new Map();
|
786
|
+
status = "running";
|
786
787
|
modules = /* @__PURE__ */ new Map();
|
787
788
|
fetch;
|
788
789
|
options;
|
@@ -805,6 +806,10 @@ var Microservice = class {
|
|
805
806
|
websocket: { enabled: false },
|
806
807
|
cacheAdapter: new MemoryCacheAdapter(),
|
807
808
|
plugins: [],
|
809
|
+
gracefulShutdown: {
|
810
|
+
timeout: 3e4,
|
811
|
+
cleanupHooks: []
|
812
|
+
},
|
808
813
|
...options
|
809
814
|
};
|
810
815
|
this.cache = this.options.cacheAdapter;
|
@@ -883,7 +888,7 @@ var Microservice = class {
|
|
883
888
|
this.app.get(prefix, (ctx) => {
|
884
889
|
const name = this.options.name ?? "Microservice";
|
885
890
|
const version = this.options.version ?? "1.0.0";
|
886
|
-
return ctx.text(`${name} is
|
891
|
+
return ctx.text(`${name} is ${this.status}. version: ${version}`);
|
887
892
|
});
|
888
893
|
this.app.get(`${prefix}/health`, (ctx) => {
|
889
894
|
return ctx.json({
|
@@ -899,7 +904,9 @@ var Microservice = class {
|
|
899
904
|
version: this.options.version,
|
900
905
|
env: this.options.env,
|
901
906
|
modules: this.getModules(),
|
902
|
-
stats: Object.fromEntries(this.statsMap)
|
907
|
+
stats: Object.fromEntries(this.statsMap),
|
908
|
+
activeRequests: this.getActiveRequestCount(),
|
909
|
+
status: this.status
|
903
910
|
});
|
904
911
|
});
|
905
912
|
this.app.get(`${prefix}/client.ts`, async (ctx) => {
|
@@ -1096,7 +1103,12 @@ var Microservice = class {
|
|
1096
1103
|
process.on("SIGINT", () => {
|
1097
1104
|
logger_default.info(`
|
1098
1105
|
Received SIGINT signal`);
|
1099
|
-
this.
|
1106
|
+
this.shutdown();
|
1107
|
+
});
|
1108
|
+
process.on("SIGTERM", () => {
|
1109
|
+
logger_default.info(`
|
1110
|
+
Received SIGTERM signal`);
|
1111
|
+
this.shutdown();
|
1100
1112
|
});
|
1101
1113
|
process.on("unhandledrejection", (event) => {
|
1102
1114
|
logger_default.error("Unhandled rejection:", event.reason);
|
@@ -1108,14 +1120,16 @@ Received SIGINT signal`);
|
|
1108
1120
|
/**
|
1109
1121
|
* 优雅停机
|
1110
1122
|
*/
|
1111
|
-
async
|
1112
|
-
if (this.
|
1113
|
-
this.
|
1114
|
-
logger_default.info("\
|
1123
|
+
async shutdown() {
|
1124
|
+
if (this.status === "shutting_down") return;
|
1125
|
+
this.status = "shutting_down";
|
1126
|
+
logger_default.info("\nshutdown initiated...");
|
1115
1127
|
if (this.statisticsTimer) clearInterval(this.statisticsTimer);
|
1116
1128
|
try {
|
1129
|
+
await this.waitForActiveRequests();
|
1130
|
+
await this.executeCleanupHooks();
|
1117
1131
|
await this.stop();
|
1118
|
-
logger_default.info("
|
1132
|
+
logger_default.info("shutdown completed");
|
1119
1133
|
} catch (error) {
|
1120
1134
|
logger_default.error("Error during shutdown:", error);
|
1121
1135
|
process.exit(1);
|
@@ -1123,6 +1137,57 @@ Received SIGINT signal`);
|
|
1123
1137
|
process.exit(0);
|
1124
1138
|
}
|
1125
1139
|
}
|
1140
|
+
/**
|
1141
|
+
* 等待所有活跃请求完成
|
1142
|
+
*/
|
1143
|
+
async waitForActiveRequests() {
|
1144
|
+
const timeout = this.options.gracefulShutdown?.timeout || 3e4;
|
1145
|
+
const startTime = Date.now();
|
1146
|
+
logger_default.info(`Waiting for ${this.activeRequests.size} active requests to complete...`);
|
1147
|
+
return new Promise((resolve) => {
|
1148
|
+
const checkInterval = setInterval(() => {
|
1149
|
+
const elapsed = Date.now() - startTime;
|
1150
|
+
if (this.activeRequests.size === 0) {
|
1151
|
+
clearInterval(checkInterval);
|
1152
|
+
logger_default.info("All active requests completed");
|
1153
|
+
resolve();
|
1154
|
+
} else if (elapsed >= timeout) {
|
1155
|
+
clearInterval(checkInterval);
|
1156
|
+
logger_default.warn(`Timeout waiting for requests to complete. ${this.activeRequests.size} requests still active`);
|
1157
|
+
resolve();
|
1158
|
+
} else {
|
1159
|
+
logger_default.info(`Still waiting for ${this.activeRequests.size} requests... (${elapsed}ms elapsed)`);
|
1160
|
+
}
|
1161
|
+
}, 1e3);
|
1162
|
+
});
|
1163
|
+
}
|
1164
|
+
/**
|
1165
|
+
* 执行清理hook
|
1166
|
+
*/
|
1167
|
+
async executeCleanupHooks() {
|
1168
|
+
const hooks = this.options.gracefulShutdown?.cleanupHooks || [];
|
1169
|
+
if (hooks.length === 0) {
|
1170
|
+
logger_default.info("No cleanup hooks configured");
|
1171
|
+
return;
|
1172
|
+
}
|
1173
|
+
logger_default.info(`Executing ${hooks.length} cleanup hooks...`);
|
1174
|
+
for (const hook of hooks) {
|
1175
|
+
try {
|
1176
|
+
const timeout = hook.timeout || 5e3;
|
1177
|
+
logger_default.info(`Executing cleanup hook: ${hook.name}`);
|
1178
|
+
await Promise.race([
|
1179
|
+
Promise.resolve(hook.cleanup()),
|
1180
|
+
new Promise(
|
1181
|
+
(_, reject) => setTimeout(() => reject(new Error(`Cleanup hook ${hook.name} timeout`)), timeout)
|
1182
|
+
)
|
1183
|
+
]);
|
1184
|
+
logger_default.info(`Cleanup hook ${hook.name} completed successfully`);
|
1185
|
+
} catch (error) {
|
1186
|
+
logger_default.error(`Cleanup hook ${hook.name} failed:`, error);
|
1187
|
+
}
|
1188
|
+
}
|
1189
|
+
logger_default.info("All cleanup hooks completed");
|
1190
|
+
}
|
1126
1191
|
initStatsEventManager() {
|
1127
1192
|
this.statisticsTimer = setInterval(async () => {
|
1128
1193
|
await this.updateStats();
|
@@ -1164,14 +1229,54 @@ Received SIGINT signal`);
|
|
1164
1229
|
}
|
1165
1230
|
return handler;
|
1166
1231
|
}
|
1232
|
+
/**
|
1233
|
+
* 添加活跃请求跟踪
|
1234
|
+
*/
|
1235
|
+
addActiveRequest(requestId, requestInfo) {
|
1236
|
+
this.activeRequests.set(requestId, requestInfo);
|
1237
|
+
}
|
1238
|
+
/**
|
1239
|
+
* 移除活跃请求跟踪
|
1240
|
+
*/
|
1241
|
+
removeActiveRequest(requestId) {
|
1242
|
+
this.activeRequests.delete(requestId);
|
1243
|
+
}
|
1244
|
+
/**
|
1245
|
+
* 获取当前活跃请求数量
|
1246
|
+
*/
|
1247
|
+
getActiveRequestCount() {
|
1248
|
+
return this.activeRequests.size;
|
1249
|
+
}
|
1250
|
+
/**
|
1251
|
+
* 获取当前活跃请求信息
|
1252
|
+
*/
|
1253
|
+
getActiveRequests() {
|
1254
|
+
return Array.from(this.activeRequests.values());
|
1255
|
+
}
|
1167
1256
|
handleRequest = async (ctx) => {
|
1168
1257
|
const { moduleName, actionName } = ctx.req.param();
|
1169
1258
|
const handler = this.getActionHandler(moduleName, actionName);
|
1259
|
+
const requestId = crypto.randomUUID();
|
1260
|
+
const startTime = Date.now();
|
1261
|
+
if (this.status === "shutting_down") {
|
1262
|
+
return ctx.json({
|
1263
|
+
success: false,
|
1264
|
+
error: "Service is shutting down"
|
1265
|
+
}, 503);
|
1266
|
+
}
|
1170
1267
|
try {
|
1171
1268
|
const paramsText = await ctx.req.text();
|
1269
|
+
this.addActiveRequest(requestId, {
|
1270
|
+
id: requestId,
|
1271
|
+
moduleName,
|
1272
|
+
actionName,
|
1273
|
+
startTime,
|
1274
|
+
params: paramsText
|
1275
|
+
});
|
1172
1276
|
const result = await handler.handle(paramsText);
|
1173
1277
|
if (handler.metadata.stream) {
|
1174
1278
|
const encoder = new TextEncoder();
|
1279
|
+
const microservice = this;
|
1175
1280
|
const stream = new ReadableStream({
|
1176
1281
|
async start(controller) {
|
1177
1282
|
try {
|
@@ -1195,6 +1300,8 @@ Received SIGINT signal`);
|
|
1195
1300
|
encoder.encode(ejson4__default.default.stringify(response) + "\n")
|
1196
1301
|
);
|
1197
1302
|
controller.close();
|
1303
|
+
} finally {
|
1304
|
+
microservice.removeActiveRequest(requestId);
|
1198
1305
|
}
|
1199
1306
|
}
|
1200
1307
|
});
|
@@ -1206,8 +1313,10 @@ Received SIGINT signal`);
|
|
1206
1313
|
}
|
1207
1314
|
});
|
1208
1315
|
}
|
1316
|
+
this.removeActiveRequest(requestId);
|
1209
1317
|
return ctx.text(ejson4__default.default.stringify({ success: true, data: result }));
|
1210
1318
|
} catch (error) {
|
1319
|
+
this.removeActiveRequest(requestId);
|
1211
1320
|
return ctx.json({ success: false, error: error.message });
|
1212
1321
|
}
|
1213
1322
|
};
|
package/dist/mod.d.cts
CHANGED
@@ -155,6 +155,32 @@ interface MicroserviceOptions {
|
|
155
155
|
};
|
156
156
|
cacheAdapter?: CacheAdapter;
|
157
157
|
plugins?: Plugin[];
|
158
|
+
gracefulShutdown?: {
|
159
|
+
/** 优雅停机超时时间(毫秒),默认 30 秒 */
|
160
|
+
timeout?: number;
|
161
|
+
/** 清理hook列表 */
|
162
|
+
cleanupHooks?: CleanupHook[];
|
163
|
+
};
|
164
|
+
}
|
165
|
+
interface CleanupHook {
|
166
|
+
/** hook名称 */
|
167
|
+
name: string;
|
168
|
+
/** 清理函数 */
|
169
|
+
cleanup: () => Promise<void> | void;
|
170
|
+
/** 超时时间(毫秒),默认 5 秒 */
|
171
|
+
timeout?: number;
|
172
|
+
}
|
173
|
+
interface RequestInfo {
|
174
|
+
/** 请求ID */
|
175
|
+
id: string;
|
176
|
+
/** 模块名称 */
|
177
|
+
moduleName: string;
|
178
|
+
/** 动作名称 */
|
179
|
+
actionName: string;
|
180
|
+
/** 开始时间 */
|
181
|
+
startTime: number;
|
182
|
+
/** 请求参数 */
|
183
|
+
params?: any;
|
158
184
|
}
|
159
185
|
interface ModuleInfo extends ModuleOptions {
|
160
186
|
actions: Record<string, ActionMetadata>;
|
@@ -197,10 +223,11 @@ declare class Microservice {
|
|
197
223
|
private lease?;
|
198
224
|
private scheduler?;
|
199
225
|
private abortController?;
|
200
|
-
private isShuttingDown;
|
201
226
|
private statisticsTimer?;
|
202
227
|
private wsHandler?;
|
203
228
|
private actionHandlers;
|
229
|
+
private activeRequests;
|
230
|
+
private status;
|
204
231
|
modules: Map<string, ModuleInfo>;
|
205
232
|
readonly fetch: typeof fetch;
|
206
233
|
options: Required<MicroserviceOptions>;
|
@@ -236,7 +263,7 @@ declare class Microservice {
|
|
236
263
|
/**
|
237
264
|
* 停止服务
|
238
265
|
*/
|
239
|
-
stop
|
266
|
+
private stop;
|
240
267
|
/**
|
241
268
|
* 初始化停机处理
|
242
269
|
*/
|
@@ -244,10 +271,34 @@ declare class Microservice {
|
|
244
271
|
/**
|
245
272
|
* 优雅停机
|
246
273
|
*/
|
247
|
-
|
274
|
+
shutdown(): Promise<void>;
|
275
|
+
/**
|
276
|
+
* 等待所有活跃请求完成
|
277
|
+
*/
|
278
|
+
private waitForActiveRequests;
|
279
|
+
/**
|
280
|
+
* 执行清理hook
|
281
|
+
*/
|
282
|
+
private executeCleanupHooks;
|
248
283
|
private initStatsEventManager;
|
249
284
|
private updateStats;
|
250
285
|
getActionHandler(moduleName: string, actionName: string): ActionHandler;
|
286
|
+
/**
|
287
|
+
* 添加活跃请求跟踪
|
288
|
+
*/
|
289
|
+
private addActiveRequest;
|
290
|
+
/**
|
291
|
+
* 移除活跃请求跟踪
|
292
|
+
*/
|
293
|
+
private removeActiveRequest;
|
294
|
+
/**
|
295
|
+
* 获取当前活跃请求数量
|
296
|
+
*/
|
297
|
+
getActiveRequestCount(): number;
|
298
|
+
/**
|
299
|
+
* 获取当前活跃请求信息
|
300
|
+
*/
|
301
|
+
getActiveRequests(): RequestInfo[];
|
251
302
|
private handleRequest;
|
252
303
|
private initPlugins;
|
253
304
|
init(): Promise<void>;
|
@@ -286,4 +337,4 @@ declare function Schedule(options: ScheduleOptions): Function;
|
|
286
337
|
|
287
338
|
declare const logger: winston.Logger;
|
288
339
|
|
289
|
-
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
340
|
+
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
package/dist/mod.d.ts
CHANGED
@@ -155,6 +155,32 @@ interface MicroserviceOptions {
|
|
155
155
|
};
|
156
156
|
cacheAdapter?: CacheAdapter;
|
157
157
|
plugins?: Plugin[];
|
158
|
+
gracefulShutdown?: {
|
159
|
+
/** 优雅停机超时时间(毫秒),默认 30 秒 */
|
160
|
+
timeout?: number;
|
161
|
+
/** 清理hook列表 */
|
162
|
+
cleanupHooks?: CleanupHook[];
|
163
|
+
};
|
164
|
+
}
|
165
|
+
interface CleanupHook {
|
166
|
+
/** hook名称 */
|
167
|
+
name: string;
|
168
|
+
/** 清理函数 */
|
169
|
+
cleanup: () => Promise<void> | void;
|
170
|
+
/** 超时时间(毫秒),默认 5 秒 */
|
171
|
+
timeout?: number;
|
172
|
+
}
|
173
|
+
interface RequestInfo {
|
174
|
+
/** 请求ID */
|
175
|
+
id: string;
|
176
|
+
/** 模块名称 */
|
177
|
+
moduleName: string;
|
178
|
+
/** 动作名称 */
|
179
|
+
actionName: string;
|
180
|
+
/** 开始时间 */
|
181
|
+
startTime: number;
|
182
|
+
/** 请求参数 */
|
183
|
+
params?: any;
|
158
184
|
}
|
159
185
|
interface ModuleInfo extends ModuleOptions {
|
160
186
|
actions: Record<string, ActionMetadata>;
|
@@ -197,10 +223,11 @@ declare class Microservice {
|
|
197
223
|
private lease?;
|
198
224
|
private scheduler?;
|
199
225
|
private abortController?;
|
200
|
-
private isShuttingDown;
|
201
226
|
private statisticsTimer?;
|
202
227
|
private wsHandler?;
|
203
228
|
private actionHandlers;
|
229
|
+
private activeRequests;
|
230
|
+
private status;
|
204
231
|
modules: Map<string, ModuleInfo>;
|
205
232
|
readonly fetch: typeof fetch;
|
206
233
|
options: Required<MicroserviceOptions>;
|
@@ -236,7 +263,7 @@ declare class Microservice {
|
|
236
263
|
/**
|
237
264
|
* 停止服务
|
238
265
|
*/
|
239
|
-
stop
|
266
|
+
private stop;
|
240
267
|
/**
|
241
268
|
* 初始化停机处理
|
242
269
|
*/
|
@@ -244,10 +271,34 @@ declare class Microservice {
|
|
244
271
|
/**
|
245
272
|
* 优雅停机
|
246
273
|
*/
|
247
|
-
|
274
|
+
shutdown(): Promise<void>;
|
275
|
+
/**
|
276
|
+
* 等待所有活跃请求完成
|
277
|
+
*/
|
278
|
+
private waitForActiveRequests;
|
279
|
+
/**
|
280
|
+
* 执行清理hook
|
281
|
+
*/
|
282
|
+
private executeCleanupHooks;
|
248
283
|
private initStatsEventManager;
|
249
284
|
private updateStats;
|
250
285
|
getActionHandler(moduleName: string, actionName: string): ActionHandler;
|
286
|
+
/**
|
287
|
+
* 添加活跃请求跟踪
|
288
|
+
*/
|
289
|
+
private addActiveRequest;
|
290
|
+
/**
|
291
|
+
* 移除活跃请求跟踪
|
292
|
+
*/
|
293
|
+
private removeActiveRequest;
|
294
|
+
/**
|
295
|
+
* 获取当前活跃请求数量
|
296
|
+
*/
|
297
|
+
getActiveRequestCount(): number;
|
298
|
+
/**
|
299
|
+
* 获取当前活跃请求信息
|
300
|
+
*/
|
301
|
+
getActiveRequests(): RequestInfo[];
|
251
302
|
private handleRequest;
|
252
303
|
private initPlugins;
|
253
304
|
init(): Promise<void>;
|
@@ -286,4 +337,4 @@ declare function Schedule(options: ScheduleOptions): Function;
|
|
286
337
|
|
287
338
|
declare const logger: winston.Logger;
|
288
339
|
|
289
|
-
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
340
|
+
export { Action, type ActionErrorEvent, type ActionMetadata, type ActionOptions, CacheAdapter, type CacheFn, type CleanupHook, type EtcdConfig, type EventServiceInfo, type McpOptions, MemoryCacheAdapter, Microservice, type MicroserviceOptions, ModelContextProtocolPlugin, Module, type ModuleInfo, type ModuleMetadata, type ModuleOptions, Plugin, type PreStartChecker, RedisCacheAdapter, type RequestInfo, Schedule, type ScheduleMetadata, ScheduleMode, type ScheduleOptions, ServiceContext, type ServiceInfo, type ServiceStats, type StatisticsEvent, type StreamResponse, logger, startCheck };
|
package/dist/mod.js
CHANGED
@@ -770,10 +770,11 @@ var Microservice = class {
|
|
770
770
|
lease;
|
771
771
|
scheduler;
|
772
772
|
abortController;
|
773
|
-
isShuttingDown = false;
|
774
773
|
statisticsTimer;
|
775
774
|
wsHandler;
|
776
775
|
actionHandlers = /* @__PURE__ */ new Map();
|
776
|
+
activeRequests = /* @__PURE__ */ new Map();
|
777
|
+
status = "running";
|
777
778
|
modules = /* @__PURE__ */ new Map();
|
778
779
|
fetch;
|
779
780
|
options;
|
@@ -796,6 +797,10 @@ var Microservice = class {
|
|
796
797
|
websocket: { enabled: false },
|
797
798
|
cacheAdapter: new MemoryCacheAdapter(),
|
798
799
|
plugins: [],
|
800
|
+
gracefulShutdown: {
|
801
|
+
timeout: 3e4,
|
802
|
+
cleanupHooks: []
|
803
|
+
},
|
799
804
|
...options
|
800
805
|
};
|
801
806
|
this.cache = this.options.cacheAdapter;
|
@@ -874,7 +879,7 @@ var Microservice = class {
|
|
874
879
|
this.app.get(prefix, (ctx) => {
|
875
880
|
const name = this.options.name ?? "Microservice";
|
876
881
|
const version = this.options.version ?? "1.0.0";
|
877
|
-
return ctx.text(`${name} is
|
882
|
+
return ctx.text(`${name} is ${this.status}. version: ${version}`);
|
878
883
|
});
|
879
884
|
this.app.get(`${prefix}/health`, (ctx) => {
|
880
885
|
return ctx.json({
|
@@ -890,7 +895,9 @@ var Microservice = class {
|
|
890
895
|
version: this.options.version,
|
891
896
|
env: this.options.env,
|
892
897
|
modules: this.getModules(),
|
893
|
-
stats: Object.fromEntries(this.statsMap)
|
898
|
+
stats: Object.fromEntries(this.statsMap),
|
899
|
+
activeRequests: this.getActiveRequestCount(),
|
900
|
+
status: this.status
|
894
901
|
});
|
895
902
|
});
|
896
903
|
this.app.get(`${prefix}/client.ts`, async (ctx) => {
|
@@ -1087,7 +1094,12 @@ var Microservice = class {
|
|
1087
1094
|
process.on("SIGINT", () => {
|
1088
1095
|
logger_default.info(`
|
1089
1096
|
Received SIGINT signal`);
|
1090
|
-
this.
|
1097
|
+
this.shutdown();
|
1098
|
+
});
|
1099
|
+
process.on("SIGTERM", () => {
|
1100
|
+
logger_default.info(`
|
1101
|
+
Received SIGTERM signal`);
|
1102
|
+
this.shutdown();
|
1091
1103
|
});
|
1092
1104
|
process.on("unhandledrejection", (event) => {
|
1093
1105
|
logger_default.error("Unhandled rejection:", event.reason);
|
@@ -1099,14 +1111,16 @@ Received SIGINT signal`);
|
|
1099
1111
|
/**
|
1100
1112
|
* 优雅停机
|
1101
1113
|
*/
|
1102
|
-
async
|
1103
|
-
if (this.
|
1104
|
-
this.
|
1105
|
-
logger_default.info("\
|
1114
|
+
async shutdown() {
|
1115
|
+
if (this.status === "shutting_down") return;
|
1116
|
+
this.status = "shutting_down";
|
1117
|
+
logger_default.info("\nshutdown initiated...");
|
1106
1118
|
if (this.statisticsTimer) clearInterval(this.statisticsTimer);
|
1107
1119
|
try {
|
1120
|
+
await this.waitForActiveRequests();
|
1121
|
+
await this.executeCleanupHooks();
|
1108
1122
|
await this.stop();
|
1109
|
-
logger_default.info("
|
1123
|
+
logger_default.info("shutdown completed");
|
1110
1124
|
} catch (error) {
|
1111
1125
|
logger_default.error("Error during shutdown:", error);
|
1112
1126
|
process.exit(1);
|
@@ -1114,6 +1128,57 @@ Received SIGINT signal`);
|
|
1114
1128
|
process.exit(0);
|
1115
1129
|
}
|
1116
1130
|
}
|
1131
|
+
/**
|
1132
|
+
* 等待所有活跃请求完成
|
1133
|
+
*/
|
1134
|
+
async waitForActiveRequests() {
|
1135
|
+
const timeout = this.options.gracefulShutdown?.timeout || 3e4;
|
1136
|
+
const startTime = Date.now();
|
1137
|
+
logger_default.info(`Waiting for ${this.activeRequests.size} active requests to complete...`);
|
1138
|
+
return new Promise((resolve) => {
|
1139
|
+
const checkInterval = setInterval(() => {
|
1140
|
+
const elapsed = Date.now() - startTime;
|
1141
|
+
if (this.activeRequests.size === 0) {
|
1142
|
+
clearInterval(checkInterval);
|
1143
|
+
logger_default.info("All active requests completed");
|
1144
|
+
resolve();
|
1145
|
+
} else if (elapsed >= timeout) {
|
1146
|
+
clearInterval(checkInterval);
|
1147
|
+
logger_default.warn(`Timeout waiting for requests to complete. ${this.activeRequests.size} requests still active`);
|
1148
|
+
resolve();
|
1149
|
+
} else {
|
1150
|
+
logger_default.info(`Still waiting for ${this.activeRequests.size} requests... (${elapsed}ms elapsed)`);
|
1151
|
+
}
|
1152
|
+
}, 1e3);
|
1153
|
+
});
|
1154
|
+
}
|
1155
|
+
/**
|
1156
|
+
* 执行清理hook
|
1157
|
+
*/
|
1158
|
+
async executeCleanupHooks() {
|
1159
|
+
const hooks = this.options.gracefulShutdown?.cleanupHooks || [];
|
1160
|
+
if (hooks.length === 0) {
|
1161
|
+
logger_default.info("No cleanup hooks configured");
|
1162
|
+
return;
|
1163
|
+
}
|
1164
|
+
logger_default.info(`Executing ${hooks.length} cleanup hooks...`);
|
1165
|
+
for (const hook of hooks) {
|
1166
|
+
try {
|
1167
|
+
const timeout = hook.timeout || 5e3;
|
1168
|
+
logger_default.info(`Executing cleanup hook: ${hook.name}`);
|
1169
|
+
await Promise.race([
|
1170
|
+
Promise.resolve(hook.cleanup()),
|
1171
|
+
new Promise(
|
1172
|
+
(_, reject) => setTimeout(() => reject(new Error(`Cleanup hook ${hook.name} timeout`)), timeout)
|
1173
|
+
)
|
1174
|
+
]);
|
1175
|
+
logger_default.info(`Cleanup hook ${hook.name} completed successfully`);
|
1176
|
+
} catch (error) {
|
1177
|
+
logger_default.error(`Cleanup hook ${hook.name} failed:`, error);
|
1178
|
+
}
|
1179
|
+
}
|
1180
|
+
logger_default.info("All cleanup hooks completed");
|
1181
|
+
}
|
1117
1182
|
initStatsEventManager() {
|
1118
1183
|
this.statisticsTimer = setInterval(async () => {
|
1119
1184
|
await this.updateStats();
|
@@ -1155,14 +1220,54 @@ Received SIGINT signal`);
|
|
1155
1220
|
}
|
1156
1221
|
return handler;
|
1157
1222
|
}
|
1223
|
+
/**
|
1224
|
+
* 添加活跃请求跟踪
|
1225
|
+
*/
|
1226
|
+
addActiveRequest(requestId, requestInfo) {
|
1227
|
+
this.activeRequests.set(requestId, requestInfo);
|
1228
|
+
}
|
1229
|
+
/**
|
1230
|
+
* 移除活跃请求跟踪
|
1231
|
+
*/
|
1232
|
+
removeActiveRequest(requestId) {
|
1233
|
+
this.activeRequests.delete(requestId);
|
1234
|
+
}
|
1235
|
+
/**
|
1236
|
+
* 获取当前活跃请求数量
|
1237
|
+
*/
|
1238
|
+
getActiveRequestCount() {
|
1239
|
+
return this.activeRequests.size;
|
1240
|
+
}
|
1241
|
+
/**
|
1242
|
+
* 获取当前活跃请求信息
|
1243
|
+
*/
|
1244
|
+
getActiveRequests() {
|
1245
|
+
return Array.from(this.activeRequests.values());
|
1246
|
+
}
|
1158
1247
|
handleRequest = async (ctx) => {
|
1159
1248
|
const { moduleName, actionName } = ctx.req.param();
|
1160
1249
|
const handler = this.getActionHandler(moduleName, actionName);
|
1250
|
+
const requestId = crypto.randomUUID();
|
1251
|
+
const startTime = Date.now();
|
1252
|
+
if (this.status === "shutting_down") {
|
1253
|
+
return ctx.json({
|
1254
|
+
success: false,
|
1255
|
+
error: "Service is shutting down"
|
1256
|
+
}, 503);
|
1257
|
+
}
|
1161
1258
|
try {
|
1162
1259
|
const paramsText = await ctx.req.text();
|
1260
|
+
this.addActiveRequest(requestId, {
|
1261
|
+
id: requestId,
|
1262
|
+
moduleName,
|
1263
|
+
actionName,
|
1264
|
+
startTime,
|
1265
|
+
params: paramsText
|
1266
|
+
});
|
1163
1267
|
const result = await handler.handle(paramsText);
|
1164
1268
|
if (handler.metadata.stream) {
|
1165
1269
|
const encoder = new TextEncoder();
|
1270
|
+
const microservice = this;
|
1166
1271
|
const stream = new ReadableStream({
|
1167
1272
|
async start(controller) {
|
1168
1273
|
try {
|
@@ -1186,6 +1291,8 @@ Received SIGINT signal`);
|
|
1186
1291
|
encoder.encode(ejson4.stringify(response) + "\n")
|
1187
1292
|
);
|
1188
1293
|
controller.close();
|
1294
|
+
} finally {
|
1295
|
+
microservice.removeActiveRequest(requestId);
|
1189
1296
|
}
|
1190
1297
|
}
|
1191
1298
|
});
|
@@ -1197,8 +1304,10 @@ Received SIGINT signal`);
|
|
1197
1304
|
}
|
1198
1305
|
});
|
1199
1306
|
}
|
1307
|
+
this.removeActiveRequest(requestId);
|
1200
1308
|
return ctx.text(ejson4.stringify({ success: true, data: result }));
|
1201
1309
|
} catch (error) {
|
1310
|
+
this.removeActiveRequest(requestId);
|
1202
1311
|
return ctx.json({ success: false, error: error.message });
|
1203
1312
|
}
|
1204
1313
|
};
|