befly 3.4.12 → 3.4.14

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/lib/database.ts CHANGED
@@ -92,6 +92,7 @@ export class Database {
92
92
  this.sqlClient = sql;
93
93
  return sql;
94
94
  } catch (error: any) {
95
+ console.log(finalUrl);
95
96
  Logger.error('数据库连接测试失败', error);
96
97
 
97
98
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.4.12",
3
+ "version": "3.4.14",
4
4
  "description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -81,5 +81,5 @@
81
81
  "ora": "^9.0.0",
82
82
  "pathe": "^2.0.3"
83
83
  },
84
- "gitHead": "7d2c21a0b69867c650cbec930e041821a1914ffb"
84
+ "gitHead": "8ced9f183b280460e8136eb00985c0fb83e63bc1"
85
85
  }
@@ -1,49 +0,0 @@
1
- /**
2
- * Cluster Worker 入口
3
- * 由 ClusterManager 启动的子进程入口文件
4
- */
5
-
6
- import { Befly } from '../main.js';
7
- import { Logger } from '../lib/logger.js';
8
-
9
- // 启动 Befly 实例
10
- const app = new Befly();
11
- const server = await app.listen();
12
-
13
- // Bun 原生信号处理:当收到 SIGTERM/SIGINT 时优雅关闭
14
- const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];
15
-
16
- signals.forEach((signal) => {
17
- process.on(signal, async () => {
18
- const workerId = process.env.CLUSTER_WORKER_ID || 'unknown';
19
- Logger.info(`Worker ${workerId} 收到 ${signal} 信号,正在关闭...`);
20
-
21
- try {
22
- // Bun Server 的 stop() 方法:关闭服务器
23
- // 参数 true 表示强制关闭(不等待现有连接)
24
- server.stop(true);
25
- Logger.info(`Worker ${workerId} HTTP 服务器已关闭`);
26
-
27
- // 给予短暂时间让资源清理完成
28
- await Bun.sleep(100);
29
-
30
- process.exit(0);
31
- } catch (error) {
32
- Logger.error(`Worker ${workerId} 关闭失败:`, error);
33
- process.exit(1);
34
- }
35
- });
36
- });
37
-
38
- // 处理未捕获的异常,防止进程意外退出
39
- process.on('uncaughtException', (error) => {
40
- const workerId = process.env.CLUSTER_WORKER_ID || 'unknown';
41
- Logger.error(`Worker ${workerId} 发生未捕获异常:`, error);
42
- // 不退出进程,让 ClusterManager 决定是否重启
43
- });
44
-
45
- process.on('unhandledRejection', (reason) => {
46
- const workerId = process.env.CLUSTER_WORKER_ID || 'unknown';
47
- Logger.error(`Worker ${workerId} 发生未处理的 Promise 拒绝:`, reason);
48
- // 不退出进程,让 ClusterManager 决定是否重启
49
- });
@@ -1,254 +0,0 @@
1
- /**
2
- * Cluster Manager - 集群管理器
3
- * 负责多进程启动、自动重启、优雅关闭
4
- */
5
-
6
- import { join } from 'pathe';
7
- import type { Subprocess } from 'bun';
8
- import { Logger } from '../lib/logger.js';
9
- import { Befly } from '../main.js';
10
-
11
- export interface ClusterOptions {
12
- /** 实例数量(数字或 'max') */
13
- instances: number | 'max';
14
- /** 起始端口 */
15
- startPort: number;
16
- /** 主机地址 */
17
- host: string;
18
- /** 项目根目录 */
19
- projectRoot: string;
20
- /** 环境变量 */
21
- env?: Record<string, string>;
22
- }
23
-
24
- interface WorkerInfo {
25
- id: number;
26
- port: number;
27
- process: Subprocess;
28
- restartCount: number;
29
- lastRestartTime: number;
30
- }
31
-
32
- export class ClusterManager {
33
- private workers: Map<number, WorkerInfo> = new Map();
34
- private isShuttingDown = false;
35
- private readonly MAX_RESTARTS = 10; // 最大重启次数
36
- private readonly RESTART_DELAY = 1000; // 重启延迟(毫秒)
37
- private readonly RESTART_WINDOW = 60000; // 重启计数窗口(1分钟)
38
-
39
- constructor(private options: ClusterOptions) {}
40
-
41
- /**
42
- * 启动集群
43
- */
44
- async start(): Promise<void> {
45
- const instances = this.getInstanceCount();
46
- const { startPort, host } = this.options;
47
-
48
- Logger.info(`启动集群模式: ${instances} 个实例\n`);
49
- Logger.info(`端口范围: ${startPort} - ${startPort + instances - 1}`);
50
- Logger.info(`主机地址: ${host}`);
51
- Logger.info(`环境: production\n`);
52
-
53
- // 启动所有 Worker
54
- for (let i = 0; i < instances; i++) {
55
- const port = startPort + i;
56
- this.spawnWorker(i, port);
57
- }
58
-
59
- // 监听进程信号
60
- this.setupSignalHandlers();
61
-
62
- Logger.info(`集群启动成功!\n`);
63
- this.printWorkerStatus();
64
- }
65
-
66
- /**
67
- * 获取实例数量
68
- */
69
- private getInstanceCount(): number {
70
- const { instances } = this.options;
71
-
72
- if (instances === 'max') {
73
- return navigator.hardwareConcurrency || 4;
74
- }
75
-
76
- const count = typeof instances === 'string' ? parseInt(instances) : instances;
77
-
78
- if (isNaN(count) || count < 1) {
79
- Logger.warn(`无效的实例数量 "${instances}",使用默认值 4`);
80
- return 4;
81
- }
82
-
83
- return count;
84
- }
85
-
86
- /**
87
- * 启动单个 Worker
88
- */
89
- private spawnWorker(id: number, port: number): void {
90
- const { projectRoot, host, env = {} } = this.options;
91
-
92
- Logger.info(`启动 Worker ${id} (端口 ${port})...`);
93
-
94
- // 使用内置的 worker 入口文件
95
- const workerFile = join(import.meta.dir, 'cluster-worker.ts');
96
-
97
- const proc = Bun.spawn(['bun', 'run', '--env-file=.env.production', workerFile], {
98
- cwd: projectRoot,
99
- stdout: 'inherit',
100
- stderr: 'inherit',
101
- stdin: 'inherit',
102
- env: {
103
- ...process.env,
104
- ...env,
105
- NODE_ENV: 'production',
106
- APP_PORT: port.toString(),
107
- APP_HOST: host,
108
- CLUSTER_MODE: '1',
109
- CLUSTER_WORKER_ID: id.toString(),
110
- CLUSTER_INSTANCES: this.getInstanceCount().toString(),
111
- FORCE_COLOR: '1'
112
- }
113
- });
114
-
115
- // 保存 Worker 信息
116
- this.workers.set(id, {
117
- id,
118
- port,
119
- process: proc,
120
- restartCount: 0,
121
- lastRestartTime: 0
122
- });
123
-
124
- // 监听进程退出
125
- this.watchWorker(id, port);
126
- }
127
-
128
- /**
129
- * 监听 Worker 退出并自动重启
130
- */
131
- private async watchWorker(id: number, port: number): Promise<void> {
132
- const worker = this.workers.get(id);
133
- if (!worker) return;
134
-
135
- const exitCode = await worker.process.exited;
136
-
137
- // 如果正在关闭,不重启
138
- if (this.isShuttingDown) {
139
- Logger.info(`Worker ${id} (端口 ${port}) 已退出`);
140
- return;
141
- }
142
-
143
- Logger.warn(`Worker ${id} (端口 ${port}) 异常退出,退出码: ${exitCode}`);
144
-
145
- // 检查重启次数
146
- if (!this.canRestart(worker)) {
147
- Logger.error(`Worker ${id} 重启次数过多,停止重启`);
148
- return;
149
- }
150
-
151
- // 延迟重启
152
- Logger.info(`${this.RESTART_DELAY / 1000} 秒后重启 Worker ${id}...`);
153
- await Bun.sleep(this.RESTART_DELAY);
154
-
155
- // 更新重启计数
156
- this.updateRestartCount(worker);
157
-
158
- // 重新启动
159
- this.spawnWorker(id, port);
160
- }
161
-
162
- /**
163
- * 检查是否可以重启
164
- */
165
- private canRestart(worker: WorkerInfo): boolean {
166
- const now = Date.now();
167
- const timeSinceLastRestart = now - worker.lastRestartTime;
168
-
169
- // 如果距离上次重启超过窗口期,重置计数
170
- if (timeSinceLastRestart > this.RESTART_WINDOW) {
171
- worker.restartCount = 0;
172
- }
173
-
174
- return worker.restartCount < this.MAX_RESTARTS;
175
- }
176
-
177
- /**
178
- * 更新重启计数
179
- */
180
- private updateRestartCount(worker: WorkerInfo): void {
181
- worker.restartCount++;
182
- worker.lastRestartTime = Date.now();
183
- }
184
-
185
- /**
186
- * 设置信号处理器
187
- */
188
- private setupSignalHandlers(): void {
189
- const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGHUP'];
190
-
191
- signals.forEach((signal) => {
192
- process.on(signal, () => {
193
- this.gracefulShutdown(signal);
194
- });
195
- });
196
- }
197
-
198
- /**
199
- * 优雅关闭
200
- */
201
- private async gracefulShutdown(signal: NodeJS.Signals): Promise<void> {
202
- if (this.isShuttingDown) return;
203
- this.isShuttingDown = true;
204
-
205
- Logger.info(`\n收到 ${signal} 信号,正在关闭集群...`);
206
-
207
- // 向所有 Worker 发送 SIGTERM
208
- for (const [id, worker] of this.workers.entries()) {
209
- Logger.info(`关闭 Worker ${id} (端口 ${worker.port})...`);
210
- try {
211
- worker.process.kill('SIGTERM');
212
- } catch (error) {
213
- Logger.warn(`无法向 Worker ${id} 发送 SIGTERM:`, error);
214
- }
215
- }
216
-
217
- // 等待所有进程退出,最多 3 秒(Bun 的 server.stop() 很快)
218
- const timeout = setTimeout(() => {
219
- Logger.warn('等待超时(3秒),强制关闭所有 Worker');
220
- for (const worker of this.workers.values()) {
221
- try {
222
- worker.process.kill('SIGKILL');
223
- } catch (error) {
224
- // 忽略 SIGKILL 失败(进程可能已退出)
225
- }
226
- }
227
- // 强制退出主进程
228
- setTimeout(() => process.exit(1), 500);
229
- }, 3000);
230
-
231
- try {
232
- // 等待所有进程退出
233
- await Promise.all(Array.from(this.workers.values()).map((w) => w.process.exited));
234
- clearTimeout(timeout);
235
- Logger.info('集群已安全关闭');
236
- process.exit(0);
237
- } catch (error) {
238
- clearTimeout(timeout);
239
- Logger.error('等待 Worker 退出时发生错误:', error);
240
- process.exit(1);
241
- }
242
- }
243
-
244
- /**
245
- * 打印 Worker 状态
246
- */
247
- private printWorkerStatus(): void {
248
- Logger.info('Worker 列表:');
249
- for (const worker of this.workers.values()) {
250
- Logger.info(` - Worker ${worker.id}: http://${this.options.host}:${worker.port}`);
251
- }
252
- Logger.info('');
253
- }
254
- }