@dangao/bun-server 1.3.0 → 1.5.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/README.md +15 -0
- package/dist/config/config-module.d.ts +17 -0
- package/dist/config/config-module.d.ts.map +1 -1
- package/dist/config/service.d.ts +18 -1
- package/dist/config/service.d.ts.map +1 -1
- package/dist/config/types.d.ts +25 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controller/controller.d.ts +5 -0
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts +42 -1
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/context.d.ts +1 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/server.d.ts +33 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6510 -4164
- package/dist/microservice/config-center/config-center-module.d.ts +43 -0
- package/dist/microservice/config-center/config-center-module.d.ts.map +1 -0
- package/dist/microservice/config-center/decorators.d.ts +58 -0
- package/dist/microservice/config-center/decorators.d.ts.map +1 -0
- package/dist/microservice/config-center/index.d.ts +9 -0
- package/dist/microservice/config-center/index.d.ts.map +1 -0
- package/dist/microservice/config-center/nacos-config-center.d.ts +37 -0
- package/dist/microservice/config-center/nacos-config-center.d.ts.map +1 -0
- package/dist/microservice/config-center/nacos-decorators.d.ts +24 -0
- package/dist/microservice/config-center/nacos-decorators.d.ts.map +1 -0
- package/dist/microservice/config-center/types.d.ts +63 -0
- package/dist/microservice/config-center/types.d.ts.map +1 -0
- package/dist/microservice/governance/circuit-breaker.d.ts +54 -0
- package/dist/microservice/governance/circuit-breaker.d.ts.map +1 -0
- package/dist/microservice/governance/decorators.d.ts +51 -0
- package/dist/microservice/governance/decorators.d.ts.map +1 -0
- package/dist/microservice/governance/index.d.ts +9 -0
- package/dist/microservice/governance/index.d.ts.map +1 -0
- package/dist/microservice/governance/rate-limiter.d.ts +26 -0
- package/dist/microservice/governance/rate-limiter.d.ts.map +1 -0
- package/dist/microservice/governance/redis-rate-limiter.d.ts +76 -0
- package/dist/microservice/governance/redis-rate-limiter.d.ts.map +1 -0
- package/dist/microservice/governance/retry-strategy.d.ts +21 -0
- package/dist/microservice/governance/retry-strategy.d.ts.map +1 -0
- package/dist/microservice/governance/types.d.ts +212 -0
- package/dist/microservice/governance/types.d.ts.map +1 -0
- package/dist/microservice/index.d.ts +10 -0
- package/dist/microservice/index.d.ts.map +1 -0
- package/dist/microservice/monitoring/index.d.ts +4 -0
- package/dist/microservice/monitoring/index.d.ts.map +1 -0
- package/dist/microservice/monitoring/metrics-collector.d.ts +54 -0
- package/dist/microservice/monitoring/metrics-collector.d.ts.map +1 -0
- package/dist/microservice/monitoring/metrics-integration.d.ts +24 -0
- package/dist/microservice/monitoring/metrics-integration.d.ts.map +1 -0
- package/dist/microservice/monitoring/types.d.ts +99 -0
- package/dist/microservice/monitoring/types.d.ts.map +1 -0
- package/dist/microservice/service-client/call-decorators.d.ts +52 -0
- package/dist/microservice/service-client/call-decorators.d.ts.map +1 -0
- package/dist/microservice/service-client/decorators.d.ts +35 -0
- package/dist/microservice/service-client/decorators.d.ts.map +1 -0
- package/dist/microservice/service-client/index.d.ts +7 -0
- package/dist/microservice/service-client/index.d.ts.map +1 -0
- package/dist/microservice/service-client/interceptors.d.ts +96 -0
- package/dist/microservice/service-client/interceptors.d.ts.map +1 -0
- package/dist/microservice/service-client/load-balancer.d.ts +59 -0
- package/dist/microservice/service-client/load-balancer.d.ts.map +1 -0
- package/dist/microservice/service-client/service-client.d.ts +74 -0
- package/dist/microservice/service-client/service-client.d.ts.map +1 -0
- package/dist/microservice/service-client/types.d.ts +155 -0
- package/dist/microservice/service-client/types.d.ts.map +1 -0
- package/dist/microservice/service-registry/decorators.d.ts +84 -0
- package/dist/microservice/service-registry/decorators.d.ts.map +1 -0
- package/dist/microservice/service-registry/discovery-decorators.d.ts +58 -0
- package/dist/microservice/service-registry/discovery-decorators.d.ts.map +1 -0
- package/dist/microservice/service-registry/health-integration.d.ts +32 -0
- package/dist/microservice/service-registry/health-integration.d.ts.map +1 -0
- package/dist/microservice/service-registry/index.d.ts +10 -0
- package/dist/microservice/service-registry/index.d.ts.map +1 -0
- package/dist/microservice/service-registry/nacos-service-registry.d.ts +68 -0
- package/dist/microservice/service-registry/nacos-service-registry.d.ts.map +1 -0
- package/dist/microservice/service-registry/service-registry-module.d.ts +48 -0
- package/dist/microservice/service-registry/service-registry-module.d.ts.map +1 -0
- package/dist/microservice/service-registry/types.d.ts +121 -0
- package/dist/microservice/service-registry/types.d.ts.map +1 -0
- package/dist/microservice/tracing/collectors.d.ts +27 -0
- package/dist/microservice/tracing/collectors.d.ts.map +1 -0
- package/dist/microservice/tracing/index.d.ts +4 -0
- package/dist/microservice/tracing/index.d.ts.map +1 -0
- package/dist/microservice/tracing/tracer.d.ts +59 -0
- package/dist/microservice/tracing/tracer.d.ts.map +1 -0
- package/dist/microservice/tracing/types.d.ts +179 -0
- package/dist/microservice/tracing/types.d.ts.map +1 -0
- package/dist/request/request.d.ts +1 -0
- package/dist/request/request.d.ts.map +1 -1
- package/docs/microservice-config-center.md +258 -0
- package/docs/microservice-nacos.md +346 -0
- package/docs/microservice-service-registry.md +306 -0
- package/docs/microservice.md +680 -0
- package/docs/troubleshooting.md +41 -0
- package/docs/zh/troubleshooting.md +41 -0
- package/package.json +5 -4
- package/src/config/config-module.ts +210 -0
- package/src/config/service.ts +52 -1
- package/src/config/types.ts +31 -0
- package/src/controller/controller.ts +8 -0
- package/src/core/application.ts +189 -3
- package/src/core/context.ts +1 -0
- package/src/core/server.ts +128 -2
- package/src/index.ts +81 -0
- package/src/microservice/config-center/config-center-module.ts +98 -0
- package/src/microservice/config-center/decorators.ts +159 -0
- package/src/microservice/config-center/index.ts +13 -0
- package/src/microservice/config-center/nacos-config-center.ts +126 -0
- package/src/microservice/config-center/nacos-decorators.ts +34 -0
- package/src/microservice/config-center/types.ts +80 -0
- package/src/microservice/governance/circuit-breaker.ts +229 -0
- package/src/microservice/governance/decorators.ts +113 -0
- package/src/microservice/governance/index.ts +18 -0
- package/src/microservice/governance/rate-limiter.ts +72 -0
- package/src/microservice/governance/redis-rate-limiter.ts +154 -0
- package/src/microservice/governance/retry-strategy.ts +74 -0
- package/src/microservice/governance/types.ts +247 -0
- package/src/microservice/index.ts +12 -0
- package/src/microservice/monitoring/index.ts +8 -0
- package/src/microservice/monitoring/metrics-collector.ts +223 -0
- package/src/microservice/monitoring/metrics-integration.ts +154 -0
- package/src/microservice/monitoring/types.ts +118 -0
- package/src/microservice/service-client/call-decorators.ts +107 -0
- package/src/microservice/service-client/decorators.ts +87 -0
- package/src/microservice/service-client/index.ts +37 -0
- package/src/microservice/service-client/interceptors.ts +182 -0
- package/src/microservice/service-client/load-balancer.ts +205 -0
- package/src/microservice/service-client/service-client.ts +488 -0
- package/src/microservice/service-client/types.ts +186 -0
- package/src/microservice/service-registry/decorators.ts +238 -0
- package/src/microservice/service-registry/discovery-decorators.ts +156 -0
- package/src/microservice/service-registry/health-integration.ts +146 -0
- package/src/microservice/service-registry/index.ts +20 -0
- package/src/microservice/service-registry/nacos-service-registry.ts +259 -0
- package/src/microservice/service-registry/service-registry-module.ts +105 -0
- package/src/microservice/service-registry/types.ts +149 -0
- package/src/microservice/tracing/collectors.ts +50 -0
- package/src/microservice/tracing/index.ts +15 -0
- package/src/microservice/tracing/tracer.ts +293 -0
- package/src/microservice/tracing/types.ts +213 -0
- package/src/request/request.ts +1 -0
- package/tests/config/set-value-by-path.test.ts +53 -0
- package/tests/core/graceful-shutdown.test.ts +321 -0
- package/tests/microservice/config-center.test.ts +77 -0
- package/tests/microservice/governance.test.ts +157 -0
- package/tests/microservice/monitoring.test.ts +75 -0
- package/tests/microservice/service-client.test.ts +136 -0
- package/tests/microservice/service-registry.test.ts +80 -0
- package/tests/microservice/tracing.test.ts +143 -0
- package/tests/utils/test-port.ts +29 -19
package/src/core/server.ts
CHANGED
|
@@ -27,6 +27,12 @@ export interface ServerOptions {
|
|
|
27
27
|
* WebSocket 网关注册表
|
|
28
28
|
*/
|
|
29
29
|
websocketRegistry?: WebSocketGatewayRegistry;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 优雅停机超时时间(毫秒)
|
|
33
|
+
* 默认 30 秒
|
|
34
|
+
*/
|
|
35
|
+
gracefulShutdownTimeout?: number;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
/**
|
|
@@ -36,6 +42,10 @@ export interface ServerOptions {
|
|
|
36
42
|
export class BunServer {
|
|
37
43
|
private server?: Server<WebSocketConnectionData>;
|
|
38
44
|
private readonly options: ServerOptions;
|
|
45
|
+
private activeRequests: number = 0;
|
|
46
|
+
private isShuttingDown: boolean = false;
|
|
47
|
+
private shutdownPromise?: Promise<void>;
|
|
48
|
+
private shutdownResolve?: () => void;
|
|
39
49
|
|
|
40
50
|
public constructor(options: ServerOptions) {
|
|
41
51
|
this.options = options;
|
|
@@ -51,6 +61,12 @@ export class BunServer {
|
|
|
51
61
|
|
|
52
62
|
const logger = LoggerManager.getLogger();
|
|
53
63
|
|
|
64
|
+
// 重置状态
|
|
65
|
+
this.activeRequests = 0;
|
|
66
|
+
this.isShuttingDown = false;
|
|
67
|
+
this.shutdownPromise = undefined;
|
|
68
|
+
this.shutdownResolve = undefined;
|
|
69
|
+
|
|
54
70
|
this.server = Bun.serve({
|
|
55
71
|
port: this.options.port ?? 3000,
|
|
56
72
|
hostname: this.options.hostname,
|
|
@@ -58,6 +74,11 @@ export class BunServer {
|
|
|
58
74
|
request: Request,
|
|
59
75
|
server: Server<WebSocketConnectionData>,
|
|
60
76
|
): Response | Promise<Response> | undefined => {
|
|
77
|
+
// 如果正在关闭,拒绝新请求
|
|
78
|
+
if (this.isShuttingDown) {
|
|
79
|
+
return new Response("Server is shutting down", { status: 503 });
|
|
80
|
+
}
|
|
81
|
+
|
|
61
82
|
const upgradeHeader = request.headers.get("upgrade");
|
|
62
83
|
if (
|
|
63
84
|
this.options.websocketRegistry &&
|
|
@@ -77,8 +98,34 @@ export class BunServer {
|
|
|
77
98
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
78
99
|
}
|
|
79
100
|
|
|
101
|
+
// 增加活跃请求计数
|
|
102
|
+
this.activeRequests++;
|
|
103
|
+
|
|
80
104
|
const context = new Context(request);
|
|
81
|
-
|
|
105
|
+
const responsePromise = this.options.fetch(context);
|
|
106
|
+
|
|
107
|
+
// 处理响应完成后的清理
|
|
108
|
+
if (responsePromise instanceof Promise) {
|
|
109
|
+
responsePromise
|
|
110
|
+
.finally(() => {
|
|
111
|
+
this.activeRequests--;
|
|
112
|
+
// 如果正在关闭且没有活跃请求,触发关闭完成
|
|
113
|
+
if (this.isShuttingDown && this.activeRequests === 0 && this.shutdownResolve) {
|
|
114
|
+
this.shutdownResolve();
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
.catch(() => {
|
|
118
|
+
// 错误已在中间件中处理,这里只负责计数
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
// 同步响应
|
|
122
|
+
this.activeRequests--;
|
|
123
|
+
if (this.isShuttingDown && this.activeRequests === 0 && this.shutdownResolve) {
|
|
124
|
+
this.shutdownResolve();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return responsePromise;
|
|
82
129
|
},
|
|
83
130
|
websocket: {
|
|
84
131
|
open: (ws) => {
|
|
@@ -99,17 +146,82 @@ export class BunServer {
|
|
|
99
146
|
}
|
|
100
147
|
|
|
101
148
|
/**
|
|
102
|
-
*
|
|
149
|
+
* 停止服务器(立即停止,不等待请求完成)
|
|
103
150
|
*/
|
|
104
151
|
public stop(): void {
|
|
105
152
|
if (this.server) {
|
|
106
153
|
const logger = LoggerManager.getLogger();
|
|
107
154
|
this.server.stop();
|
|
108
155
|
this.server = undefined;
|
|
156
|
+
this.isShuttingDown = false;
|
|
157
|
+
this.activeRequests = 0;
|
|
109
158
|
logger.info("Server stopped");
|
|
110
159
|
}
|
|
111
160
|
}
|
|
112
161
|
|
|
162
|
+
/**
|
|
163
|
+
* 优雅停机
|
|
164
|
+
* 停止接受新请求,等待正在处理的请求完成
|
|
165
|
+
* @param timeout - 超时时间(毫秒),默认使用配置的 gracefulShutdownTimeout 或 30000
|
|
166
|
+
* @returns Promise,在停机完成时 resolve
|
|
167
|
+
*/
|
|
168
|
+
public async gracefulShutdown(timeout?: number): Promise<void> {
|
|
169
|
+
if (!this.server || this.isShuttingDown) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const logger = LoggerManager.getLogger();
|
|
174
|
+
const shutdownTimeout = timeout ?? this.options.gracefulShutdownTimeout ?? 30000;
|
|
175
|
+
|
|
176
|
+
logger.info(`Starting graceful shutdown (timeout: ${shutdownTimeout}ms, active requests: ${this.activeRequests})`);
|
|
177
|
+
|
|
178
|
+
// 标记为正在关闭,停止接受新请求
|
|
179
|
+
this.isShuttingDown = true;
|
|
180
|
+
|
|
181
|
+
// 如果没有活跃请求,立即关闭
|
|
182
|
+
if (this.activeRequests === 0) {
|
|
183
|
+
this.stop();
|
|
184
|
+
logger.info("Graceful shutdown completed (no active requests)");
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 创建关闭 Promise
|
|
189
|
+
if (!this.shutdownPromise) {
|
|
190
|
+
this.shutdownPromise = new Promise<void>((resolve) => {
|
|
191
|
+
this.shutdownResolve = resolve;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 设置超时
|
|
196
|
+
const timeoutPromise = new Promise<void>((resolve) => {
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
logger.warn(`Graceful shutdown timeout (${shutdownTimeout}ms), forcing shutdown`);
|
|
199
|
+
resolve();
|
|
200
|
+
}, shutdownTimeout);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// 等待所有请求完成或超时
|
|
204
|
+
await Promise.race([this.shutdownPromise, timeoutPromise]);
|
|
205
|
+
|
|
206
|
+
// 停止服务器
|
|
207
|
+
this.stop();
|
|
208
|
+
logger.info(`Graceful shutdown completed (remaining active requests: ${this.activeRequests})`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 获取当前活跃请求数
|
|
213
|
+
*/
|
|
214
|
+
public getActiveRequests(): number {
|
|
215
|
+
return this.activeRequests;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 检查是否正在关闭
|
|
220
|
+
*/
|
|
221
|
+
public isShuttingDownState(): boolean {
|
|
222
|
+
return this.isShuttingDown;
|
|
223
|
+
}
|
|
224
|
+
|
|
113
225
|
/**
|
|
114
226
|
* 获取服务器实例
|
|
115
227
|
* @returns Bun Server 实例
|
|
@@ -125,4 +237,18 @@ export class BunServer {
|
|
|
125
237
|
public isRunning(): boolean {
|
|
126
238
|
return this.server !== undefined;
|
|
127
239
|
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 获取服务器端口
|
|
243
|
+
*/
|
|
244
|
+
public getPort(): number {
|
|
245
|
+
return this.options.port ?? 3000;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 获取服务器主机名
|
|
250
|
+
*/
|
|
251
|
+
public getHostname(): string | undefined {
|
|
252
|
+
return this.options.hostname;
|
|
253
|
+
}
|
|
128
254
|
}
|
package/src/index.ts
CHANGED
|
@@ -306,4 +306,85 @@ export type {
|
|
|
306
306
|
SessionData,
|
|
307
307
|
RedisSessionStoreOptions,
|
|
308
308
|
} from './session';
|
|
309
|
+
// Microservice 模块
|
|
310
|
+
export {
|
|
311
|
+
ConfigCenterModule,
|
|
312
|
+
NacosConfigCenter,
|
|
313
|
+
CONFIG_CENTER_TOKEN,
|
|
314
|
+
type ConfigCenterModuleOptions,
|
|
315
|
+
type ConfigCenterProvider,
|
|
316
|
+
type NacosConfigCenterOptions,
|
|
317
|
+
type ConfigCenter,
|
|
318
|
+
type ConfigChangeListener,
|
|
319
|
+
type ConfigResult,
|
|
320
|
+
} from './microservice';
|
|
321
|
+
export {
|
|
322
|
+
ServiceRegistryModule,
|
|
323
|
+
NacosServiceRegistry,
|
|
324
|
+
SERVICE_REGISTRY_TOKEN,
|
|
325
|
+
type ServiceRegistryModuleOptions,
|
|
326
|
+
type ServiceRegistryProvider,
|
|
327
|
+
type NacosServiceRegistryOptions,
|
|
328
|
+
type ServiceRegistry,
|
|
329
|
+
type GetInstancesOptions,
|
|
330
|
+
type InstancesChangeListener,
|
|
331
|
+
type ServiceInstance,
|
|
332
|
+
} from './microservice';
|
|
333
|
+
// Service Client 模块
|
|
334
|
+
export {
|
|
335
|
+
ServiceClient,
|
|
336
|
+
LoadBalancerFactory,
|
|
337
|
+
RandomLoadBalancer,
|
|
338
|
+
RoundRobinLoadBalancer,
|
|
339
|
+
WeightedRoundRobinLoadBalancer,
|
|
340
|
+
ConsistentHashLoadBalancer,
|
|
341
|
+
LeastActiveLoadBalancer,
|
|
342
|
+
TraceIdRequestInterceptor,
|
|
343
|
+
UserInfoRequestInterceptor,
|
|
344
|
+
RequestLogInterceptor,
|
|
345
|
+
ResponseLogInterceptor,
|
|
346
|
+
ResponseTransformInterceptor,
|
|
347
|
+
ErrorHandlerInterceptor,
|
|
348
|
+
type LoadBalanceStrategy,
|
|
349
|
+
type LoadBalancer,
|
|
350
|
+
type ServiceCallOptions,
|
|
351
|
+
type ServiceCallResponse,
|
|
352
|
+
type ServiceRequestInterceptor,
|
|
353
|
+
type ServiceResponseInterceptor,
|
|
354
|
+
type ServiceCallError,
|
|
355
|
+
} from './microservice';
|
|
356
|
+
// Governance 模块
|
|
357
|
+
export {
|
|
358
|
+
CircuitBreaker,
|
|
359
|
+
CircuitBreakerState,
|
|
360
|
+
RateLimiter,
|
|
361
|
+
RetryStrategyImpl,
|
|
362
|
+
type CircuitBreakerOptions,
|
|
363
|
+
type CircuitBreakerStats,
|
|
364
|
+
type RateLimiterOptions,
|
|
365
|
+
type RetryStrategy,
|
|
366
|
+
} from './microservice';
|
|
367
|
+
// Tracing 模块
|
|
368
|
+
export {
|
|
369
|
+
Tracer,
|
|
370
|
+
ConsoleTraceCollector,
|
|
371
|
+
MemoryTraceCollector,
|
|
372
|
+
SpanStatus,
|
|
373
|
+
SpanKind,
|
|
374
|
+
type TraceId,
|
|
375
|
+
type SpanId,
|
|
376
|
+
type SpanContext,
|
|
377
|
+
type Span,
|
|
378
|
+
type SpanTags,
|
|
379
|
+
type SpanEvent,
|
|
380
|
+
type TracingOptions,
|
|
381
|
+
type TraceCollector,
|
|
382
|
+
} from './microservice';
|
|
383
|
+
// Monitoring 模块
|
|
384
|
+
export {
|
|
385
|
+
ServiceMetricsCollector,
|
|
386
|
+
type ServiceCallMetrics,
|
|
387
|
+
type ServiceInstanceHealth,
|
|
388
|
+
type MonitoringOptions,
|
|
389
|
+
} from './microservice';
|
|
309
390
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Module, MODULE_METADATA_KEY, type ModuleProvider } from '../../di/module';
|
|
2
|
+
import { NacosClient } from '@dangao/nacos-client';
|
|
3
|
+
import type { NacosClientOptions } from '@dangao/nacos-client';
|
|
4
|
+
import { NacosConfigCenter } from './nacos-config-center';
|
|
5
|
+
import { CONFIG_CENTER_TOKEN, type ConfigCenter } from './types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 配置中心 Provider 类型
|
|
9
|
+
*/
|
|
10
|
+
export type ConfigCenterProvider = 'nacos' | 'consul' | 'etcd' | 'apollo';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Nacos 配置中心选项
|
|
14
|
+
*/
|
|
15
|
+
export interface NacosConfigCenterOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Nacos 客户端配置
|
|
18
|
+
*/
|
|
19
|
+
client: NacosClientOptions;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 配置监听轮询间隔(毫秒)
|
|
23
|
+
* @default 3000
|
|
24
|
+
*/
|
|
25
|
+
watchInterval?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 配置中心模块选项
|
|
30
|
+
*/
|
|
31
|
+
export interface ConfigCenterModuleOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Provider 类型
|
|
34
|
+
*/
|
|
35
|
+
provider: ConfigCenterProvider;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Nacos 配置(当 provider 为 'nacos' 时使用)
|
|
39
|
+
*/
|
|
40
|
+
nacos?: NacosConfigCenterOptions;
|
|
41
|
+
|
|
42
|
+
// 未来可以添加其他 provider 的配置
|
|
43
|
+
// consul?: ConsulConfigCenterOptions;
|
|
44
|
+
// etcd?: EtcdConfigCenterOptions;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 配置中心模块
|
|
49
|
+
*/
|
|
50
|
+
@Module({
|
|
51
|
+
providers: [],
|
|
52
|
+
})
|
|
53
|
+
export class ConfigCenterModule {
|
|
54
|
+
/**
|
|
55
|
+
* 创建配置中心模块
|
|
56
|
+
* @param options - 模块配置
|
|
57
|
+
*/
|
|
58
|
+
public static forRoot(options: ConfigCenterModuleOptions): typeof ConfigCenterModule {
|
|
59
|
+
const providers: ModuleProvider[] = [];
|
|
60
|
+
|
|
61
|
+
let configCenter: ConfigCenter;
|
|
62
|
+
|
|
63
|
+
switch (options.provider) {
|
|
64
|
+
case 'nacos':
|
|
65
|
+
if (!options.nacos) {
|
|
66
|
+
throw new Error('Nacos configuration is required when provider is "nacos"');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const nacosClient = new NacosClient(options.nacos.client);
|
|
70
|
+
configCenter = new NacosConfigCenter(nacosClient, {
|
|
71
|
+
watchInterval: options.nacos.watchInterval,
|
|
72
|
+
});
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
default:
|
|
76
|
+
throw new Error(`Unsupported config center provider: ${options.provider}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
providers.push({
|
|
80
|
+
provide: CONFIG_CENTER_TOKEN,
|
|
81
|
+
useValue: configCenter,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, ConfigCenterModule) || {};
|
|
85
|
+
const metadata = {
|
|
86
|
+
...existingMetadata,
|
|
87
|
+
providers: [...(existingMetadata.providers || []), ...providers],
|
|
88
|
+
exports: [
|
|
89
|
+
...(existingMetadata.exports || []),
|
|
90
|
+
CONFIG_CENTER_TOKEN,
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, ConfigCenterModule);
|
|
94
|
+
|
|
95
|
+
return ConfigCenterModule;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { ConfigCenter } from './types';
|
|
3
|
+
import { CONFIG_CENTER_TOKEN } from './types';
|
|
4
|
+
import { ControllerRegistry } from '../../controller/controller';
|
|
5
|
+
import type { Constructor } from '../../core/types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 配置中心值装饰器元数据键
|
|
9
|
+
*/
|
|
10
|
+
const CONFIG_CENTER_VALUE_METADATA_KEY = Symbol('config-center:value');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 配置中心值元数据
|
|
14
|
+
*/
|
|
15
|
+
export interface ConfigCenterValueMetadata {
|
|
16
|
+
/**
|
|
17
|
+
* 配置 dataId
|
|
18
|
+
*/
|
|
19
|
+
dataId: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 配置分组
|
|
23
|
+
*/
|
|
24
|
+
groupName: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 命名空间(可选)
|
|
28
|
+
*/
|
|
29
|
+
namespaceId?: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 默认值(可选)
|
|
33
|
+
*/
|
|
34
|
+
defaultValue?: string;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 是否监听配置变更
|
|
38
|
+
*/
|
|
39
|
+
watch?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* ConfigCenterValue 装饰器
|
|
44
|
+
* 用于自动注入配置中心的值
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* @Injectable()
|
|
49
|
+
* class MyService {
|
|
50
|
+
* @ConfigCenterValue('my-config', 'DEFAULT_GROUP')
|
|
51
|
+
* public configValue: string = '';
|
|
52
|
+
*
|
|
53
|
+
* @ConfigCenterValue('app-name', 'DEFAULT_GROUP', { defaultValue: 'MyApp' })
|
|
54
|
+
* public appName: string = '';
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function ConfigCenterValue(
|
|
59
|
+
dataId: string,
|
|
60
|
+
groupName: string = 'DEFAULT_GROUP',
|
|
61
|
+
options?: {
|
|
62
|
+
namespaceId?: string;
|
|
63
|
+
defaultValue?: string;
|
|
64
|
+
watch?: boolean;
|
|
65
|
+
},
|
|
66
|
+
): PropertyDecorator {
|
|
67
|
+
return function (target: object, propertyKey: string | symbol) {
|
|
68
|
+
const metadata: ConfigCenterValueMetadata = {
|
|
69
|
+
dataId,
|
|
70
|
+
groupName,
|
|
71
|
+
namespaceId: options?.namespaceId,
|
|
72
|
+
defaultValue: options?.defaultValue,
|
|
73
|
+
watch: options?.watch ?? false,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// 保存元数据
|
|
77
|
+
const existingMetadata: Map<string | symbol, ConfigCenterValueMetadata> =
|
|
78
|
+
Reflect.getMetadata(CONFIG_CENTER_VALUE_METADATA_KEY, target.constructor) ||
|
|
79
|
+
new Map();
|
|
80
|
+
existingMetadata.set(propertyKey, metadata);
|
|
81
|
+
Reflect.defineMetadata(
|
|
82
|
+
CONFIG_CENTER_VALUE_METADATA_KEY,
|
|
83
|
+
existingMetadata,
|
|
84
|
+
target.constructor,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// 定义属性描述符,实现自动注入
|
|
88
|
+
// 注意:属性 getter 必须是同步的,不能是 async
|
|
89
|
+
// 实际值通过 initializeConfigCenterValues 在实例化时预加载
|
|
90
|
+
let value: string | undefined = options?.defaultValue;
|
|
91
|
+
|
|
92
|
+
Object.defineProperty(target, propertyKey, {
|
|
93
|
+
get: () => {
|
|
94
|
+
// 同步返回已加载的值(如果还未加载,返回默认值)
|
|
95
|
+
return value ?? options?.defaultValue ?? '';
|
|
96
|
+
},
|
|
97
|
+
set: (newValue: string) => {
|
|
98
|
+
value = newValue;
|
|
99
|
+
},
|
|
100
|
+
enumerable: true,
|
|
101
|
+
configurable: true,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 获取类的所有配置中心值元数据
|
|
108
|
+
*/
|
|
109
|
+
export function getConfigCenterValueMetadata(
|
|
110
|
+
target: Constructor<unknown>,
|
|
111
|
+
): Map<string | symbol, ConfigCenterValueMetadata> {
|
|
112
|
+
return (
|
|
113
|
+
Reflect.getMetadata(CONFIG_CENTER_VALUE_METADATA_KEY, target) || new Map()
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 初始化类的配置中心值
|
|
119
|
+
* 在类实例化后调用,自动加载配置值
|
|
120
|
+
*/
|
|
121
|
+
export async function initializeConfigCenterValues(
|
|
122
|
+
instance: object,
|
|
123
|
+
target: Constructor<unknown>,
|
|
124
|
+
): Promise<void> {
|
|
125
|
+
const metadata = getConfigCenterValueMetadata(target);
|
|
126
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
127
|
+
const configCenter = container.resolve<ConfigCenter>(CONFIG_CENTER_TOKEN);
|
|
128
|
+
|
|
129
|
+
if (!configCenter) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (const [propertyKey, meta] of metadata.entries()) {
|
|
134
|
+
try {
|
|
135
|
+
const result = await configCenter.getConfig(
|
|
136
|
+
meta.dataId,
|
|
137
|
+
meta.groupName,
|
|
138
|
+
meta.namespaceId,
|
|
139
|
+
);
|
|
140
|
+
(instance as any)[propertyKey] = result.content;
|
|
141
|
+
|
|
142
|
+
// 如果启用监听,设置监听器
|
|
143
|
+
if (meta.watch) {
|
|
144
|
+
configCenter.watchConfig(meta.dataId, meta.groupName, (newResult: any) => {
|
|
145
|
+
(instance as any)[propertyKey] = newResult.content;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(
|
|
150
|
+
`[ConfigCenterValue] Failed to initialize config ${meta.dataId}:`,
|
|
151
|
+
error,
|
|
152
|
+
);
|
|
153
|
+
if (meta.defaultValue !== undefined) {
|
|
154
|
+
(instance as any)[propertyKey] = meta.defaultValue;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { ConfigCenterModule } from './config-center-module';
|
|
2
|
+
export type { ConfigCenterModuleOptions, ConfigCenterProvider, NacosConfigCenterOptions } from './config-center-module';
|
|
3
|
+
export { NacosConfigCenter } from './nacos-config-center';
|
|
4
|
+
export { CONFIG_CENTER_TOKEN } from './types';
|
|
5
|
+
export type { ConfigCenter, ConfigChangeListener, ConfigResult } from './types';
|
|
6
|
+
export {
|
|
7
|
+
ConfigCenterValue,
|
|
8
|
+
getConfigCenterValueMetadata,
|
|
9
|
+
initializeConfigCenterValues,
|
|
10
|
+
} from './decorators';
|
|
11
|
+
export { NacosValue } from './nacos-decorators';
|
|
12
|
+
export type { ConfigCenterValueMetadata } from './decorators';
|
|
13
|
+
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { NacosClient, NacosConfigClient } from '@dangao/nacos-client';
|
|
2
|
+
import type { ConfigResult, ConfigChangeListener, ConfigCenter } from './types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Nacos 配置中心实现
|
|
6
|
+
* 实现 ConfigCenter 接口,内部使用 @dangao/nacos-client
|
|
7
|
+
*/
|
|
8
|
+
export class NacosConfigCenter implements ConfigCenter {
|
|
9
|
+
private readonly client: NacosClient;
|
|
10
|
+
private readonly configClient: NacosConfigClient;
|
|
11
|
+
private readonly watchers: Map<string, { listener: ConfigChangeListener; intervalId: number }> = new Map();
|
|
12
|
+
private readonly watchInterval: number = 3000; // 默认 3 秒轮询一次
|
|
13
|
+
|
|
14
|
+
public constructor(
|
|
15
|
+
client: NacosClient,
|
|
16
|
+
options?: {
|
|
17
|
+
/**
|
|
18
|
+
* 配置监听轮询间隔(毫秒)
|
|
19
|
+
* @default 3000
|
|
20
|
+
*/
|
|
21
|
+
watchInterval?: number;
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
24
|
+
this.client = client;
|
|
25
|
+
this.configClient = new NacosConfigClient(client);
|
|
26
|
+
this.watchInterval = options?.watchInterval ?? 3000;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 获取配置
|
|
31
|
+
*/
|
|
32
|
+
public async getConfig(
|
|
33
|
+
dataId: string,
|
|
34
|
+
groupName: string,
|
|
35
|
+
namespaceId?: string,
|
|
36
|
+
): Promise<ConfigResult> {
|
|
37
|
+
const result = await this.configClient.getConfig({
|
|
38
|
+
dataId,
|
|
39
|
+
groupName,
|
|
40
|
+
namespaceId,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
content: result.content,
|
|
45
|
+
md5: result.md5,
|
|
46
|
+
lastModified: result.lastModified,
|
|
47
|
+
contentType: result.contentType,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 监听配置变更
|
|
53
|
+
* 通过轮询和 md5 比对实现配置热更新
|
|
54
|
+
*/
|
|
55
|
+
public watchConfig(
|
|
56
|
+
dataId: string,
|
|
57
|
+
groupName: string,
|
|
58
|
+
listener: ConfigChangeListener,
|
|
59
|
+
namespaceId?: string,
|
|
60
|
+
): () => void {
|
|
61
|
+
const key = this.getWatchKey(dataId, groupName, namespaceId);
|
|
62
|
+
|
|
63
|
+
// 如果已经存在监听器,先取消
|
|
64
|
+
const existing = this.watchers.get(key);
|
|
65
|
+
if (existing) {
|
|
66
|
+
clearInterval(existing.intervalId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let lastMd5: string | undefined;
|
|
70
|
+
|
|
71
|
+
const intervalId = setInterval(async () => {
|
|
72
|
+
try {
|
|
73
|
+
const result = await this.getConfig(dataId, groupName, namespaceId);
|
|
74
|
+
|
|
75
|
+
// 通过 md5 判断配置是否变更
|
|
76
|
+
if (lastMd5 === undefined || lastMd5 !== result.md5) {
|
|
77
|
+
lastMd5 = result.md5;
|
|
78
|
+
listener(result);
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// 监听错误不抛出,避免影响其他监听器
|
|
82
|
+
console.error(`[NacosConfigCenter] Failed to watch config ${dataId}:`, error);
|
|
83
|
+
}
|
|
84
|
+
}, this.watchInterval) as unknown as number;
|
|
85
|
+
|
|
86
|
+
// 立即获取一次配置
|
|
87
|
+
this.getConfig(dataId, groupName, namespaceId)
|
|
88
|
+
.then((result) => {
|
|
89
|
+
lastMd5 = result.md5;
|
|
90
|
+
listener(result);
|
|
91
|
+
})
|
|
92
|
+
.catch((error) => {
|
|
93
|
+
console.error(`[NacosConfigCenter] Failed to get initial config ${dataId}:`, error);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
this.watchers.set(key, { listener, intervalId });
|
|
97
|
+
|
|
98
|
+
// 返回取消监听的函数
|
|
99
|
+
return () => {
|
|
100
|
+
const watcher = this.watchers.get(key);
|
|
101
|
+
if (watcher) {
|
|
102
|
+
clearInterval(watcher.intervalId);
|
|
103
|
+
this.watchers.delete(key);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 关闭配置中心连接
|
|
110
|
+
*/
|
|
111
|
+
public async close(): Promise<void> {
|
|
112
|
+
// 清除所有监听器
|
|
113
|
+
for (const watcher of this.watchers.values()) {
|
|
114
|
+
clearInterval(watcher.intervalId);
|
|
115
|
+
}
|
|
116
|
+
this.watchers.clear();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 生成监听器 key
|
|
121
|
+
*/
|
|
122
|
+
private getWatchKey(dataId: string, groupName: string, namespaceId?: string): string {
|
|
123
|
+
return `${namespaceId ?? 'default'}:${groupName}:${dataId}`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ConfigCenterValue } from './decorators';
|
|
3
|
+
import type { Constructor } from '../../core/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* NacosValue 装饰器
|
|
7
|
+
* Nacos 特定的配置值注入装饰器
|
|
8
|
+
* 这是 @ConfigCenterValue 的便捷别名,专门用于 Nacos
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* @Injectable()
|
|
13
|
+
* class MyService {
|
|
14
|
+
* @NacosValue('my-config', 'DEFAULT_GROUP')
|
|
15
|
+
* public configValue: string = '';
|
|
16
|
+
*
|
|
17
|
+
* @NacosValue('app-name', 'DEFAULT_GROUP', { defaultValue: 'MyApp' })
|
|
18
|
+
* public appName: string = '';
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function NacosValue(
|
|
23
|
+
dataId: string,
|
|
24
|
+
groupName: string = 'DEFAULT_GROUP',
|
|
25
|
+
options?: {
|
|
26
|
+
namespaceId?: string;
|
|
27
|
+
defaultValue?: string;
|
|
28
|
+
watch?: boolean;
|
|
29
|
+
},
|
|
30
|
+
): PropertyDecorator {
|
|
31
|
+
// 直接使用 ConfigCenterValue,因为 NacosConfigCenter 实现了 ConfigCenter 接口
|
|
32
|
+
return ConfigCenterValue(dataId, groupName, options);
|
|
33
|
+
}
|
|
34
|
+
|