@dangao/bun-server 1.2.0 → 1.4.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/controller/decorators.d.ts +30 -1
- package/dist/controller/decorators.d.ts.map +1 -1
- package/dist/controller/index.d.ts +2 -2
- package/dist/controller/index.d.ts.map +1 -1
- package/dist/controller/param-binder.d.ts +12 -0
- package/dist/controller/param-binder.d.ts.map +1 -1
- package/dist/core/application.d.ts +15 -0
- 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 +8 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6487 -4178
- 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/controller/decorators.ts +55 -0
- package/src/controller/index.ts +16 -2
- package/src/controller/param-binder.ts +87 -1
- package/src/core/application.ts +100 -2
- package/src/core/context.ts +1 -0
- package/src/core/server.ts +14 -0
- package/src/index.ts +98 -2
- 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/controller/param-map.test.ts +237 -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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { RetryStrategy } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 重试策略实现
|
|
5
|
+
*/
|
|
6
|
+
export class RetryStrategyImpl {
|
|
7
|
+
private readonly options: Required<Pick<RetryStrategy, 'maxRetries' | 'retryDelay' | 'exponentialBackoff' | 'baseDelay' | 'maxDelay'>> & {
|
|
8
|
+
shouldRetry?: RetryStrategy['shouldRetry'];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
public constructor(options: RetryStrategy = {}) {
|
|
12
|
+
this.options = {
|
|
13
|
+
maxRetries: options.maxRetries ?? 3,
|
|
14
|
+
retryDelay: options.retryDelay ?? 1000,
|
|
15
|
+
exponentialBackoff: options.exponentialBackoff ?? false,
|
|
16
|
+
baseDelay: options.baseDelay ?? 1000,
|
|
17
|
+
maxDelay: options.maxDelay ?? 30000,
|
|
18
|
+
shouldRetry: options.shouldRetry,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 执行带重试的请求
|
|
24
|
+
*/
|
|
25
|
+
public async execute<T>(fn: () => Promise<T>): Promise<T> {
|
|
26
|
+
let lastError: Error | undefined;
|
|
27
|
+
|
|
28
|
+
for (let attempt = 0; attempt <= this.options.maxRetries; attempt++) {
|
|
29
|
+
try {
|
|
30
|
+
return await fn();
|
|
31
|
+
} catch (error) {
|
|
32
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
33
|
+
|
|
34
|
+
// 检查是否应该重试
|
|
35
|
+
if (attempt >= this.options.maxRetries) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (this.options.shouldRetry && !this.options.shouldRetry(lastError)) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 计算延迟时间
|
|
44
|
+
const delay = this.calculateDelay(attempt);
|
|
45
|
+
|
|
46
|
+
// 等待重试
|
|
47
|
+
await this.sleep(delay);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
throw lastError ?? new Error('Retry failed');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 计算重试延迟
|
|
56
|
+
*/
|
|
57
|
+
private calculateDelay(attempt: number): number {
|
|
58
|
+
if (!this.options.exponentialBackoff) {
|
|
59
|
+
return this.options.retryDelay;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 指数退避:baseDelay * 2^attempt
|
|
63
|
+
const delay = this.options.baseDelay * Math.pow(2, attempt);
|
|
64
|
+
return Math.min(delay, this.options.maxDelay);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 休眠
|
|
69
|
+
*/
|
|
70
|
+
private sleep(ms: number): Promise<void> {
|
|
71
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 熔断器状态
|
|
3
|
+
*/
|
|
4
|
+
export enum CircuitBreakerState {
|
|
5
|
+
/**
|
|
6
|
+
* 关闭状态:正常处理请求
|
|
7
|
+
*/
|
|
8
|
+
CLOSED = 'CLOSED',
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 开启状态:拒绝请求,直接返回错误
|
|
12
|
+
*/
|
|
13
|
+
OPEN = 'OPEN',
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 半开状态:允许少量请求通过,用于测试服务是否恢复
|
|
17
|
+
*/
|
|
18
|
+
HALF_OPEN = 'HALF_OPEN',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 熔断器配置选项
|
|
23
|
+
*/
|
|
24
|
+
export interface CircuitBreakerOptions {
|
|
25
|
+
/**
|
|
26
|
+
* 失败率阈值(0-1)
|
|
27
|
+
* 当失败率超过此值时,熔断器开启
|
|
28
|
+
* @default 0.5
|
|
29
|
+
*/
|
|
30
|
+
failureThreshold?: number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 时间窗口(毫秒)
|
|
34
|
+
* 在此时间窗口内统计失败率
|
|
35
|
+
* @default 60000
|
|
36
|
+
*/
|
|
37
|
+
timeWindow?: number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 最小请求数
|
|
41
|
+
* 在时间窗口内至少需要这么多请求才会触发熔断
|
|
42
|
+
* @default 10
|
|
43
|
+
*/
|
|
44
|
+
minimumRequests?: number;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 熔断持续时间(毫秒)
|
|
48
|
+
* 熔断器开启后,持续多长时间才进入半开状态
|
|
49
|
+
* @default 60000
|
|
50
|
+
*/
|
|
51
|
+
openDuration?: number;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 半开状态下的测试请求数
|
|
55
|
+
* 在半开状态下允许通过的请求数
|
|
56
|
+
* @default 3
|
|
57
|
+
*/
|
|
58
|
+
halfOpenRequests?: number;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 超时时间(毫秒)
|
|
62
|
+
* 请求超过此时间视为失败
|
|
63
|
+
* @default 5000
|
|
64
|
+
*/
|
|
65
|
+
timeout?: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 熔断器统计信息
|
|
70
|
+
*/
|
|
71
|
+
export interface CircuitBreakerStats {
|
|
72
|
+
/**
|
|
73
|
+
* 当前状态
|
|
74
|
+
*/
|
|
75
|
+
state: CircuitBreakerState;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 总请求数
|
|
79
|
+
*/
|
|
80
|
+
totalRequests: number;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 成功请求数
|
|
84
|
+
*/
|
|
85
|
+
successRequests: number;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 失败请求数
|
|
89
|
+
*/
|
|
90
|
+
failureRequests: number;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 当前失败率
|
|
94
|
+
*/
|
|
95
|
+
failureRate: number;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 最后失败时间
|
|
99
|
+
*/
|
|
100
|
+
lastFailureTime?: number;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 熔断开启时间
|
|
104
|
+
*/
|
|
105
|
+
openTime?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 限流器配置选项
|
|
110
|
+
*/
|
|
111
|
+
export interface RateLimiterOptions {
|
|
112
|
+
/**
|
|
113
|
+
* 每秒允许的请求数
|
|
114
|
+
* @default 100
|
|
115
|
+
*/
|
|
116
|
+
requestsPerSecond?: number;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 时间窗口(毫秒)
|
|
120
|
+
* @default 1000
|
|
121
|
+
*/
|
|
122
|
+
timeWindow?: number;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 是否使用分布式限流(需要 Redis)
|
|
126
|
+
* @default false
|
|
127
|
+
*/
|
|
128
|
+
distributed?: boolean;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Redis 配置(分布式限流时使用)
|
|
132
|
+
*/
|
|
133
|
+
redis?: {
|
|
134
|
+
host: string;
|
|
135
|
+
port: number;
|
|
136
|
+
password?: string;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 重试策略配置
|
|
142
|
+
*/
|
|
143
|
+
export interface RetryStrategy {
|
|
144
|
+
/**
|
|
145
|
+
* 最大重试次数
|
|
146
|
+
* @default 3
|
|
147
|
+
*/
|
|
148
|
+
maxRetries?: number;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* 重试延迟(毫秒)
|
|
152
|
+
* @default 1000
|
|
153
|
+
*/
|
|
154
|
+
retryDelay?: number;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* 是否使用指数退避
|
|
158
|
+
* @default false
|
|
159
|
+
*/
|
|
160
|
+
exponentialBackoff?: boolean;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 指数退避的基础延迟(毫秒)
|
|
164
|
+
* @default 1000
|
|
165
|
+
*/
|
|
166
|
+
baseDelay?: number;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 指数退避的最大延迟(毫秒)
|
|
170
|
+
* @default 30000
|
|
171
|
+
*/
|
|
172
|
+
maxDelay?: number;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 重试条件:哪些错误应该重试
|
|
176
|
+
* @param error - 错误对象
|
|
177
|
+
* @returns 是否应该重试
|
|
178
|
+
*/
|
|
179
|
+
shouldRetry?: (error: Error) => boolean;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 重试决策
|
|
184
|
+
*/
|
|
185
|
+
export interface RetryDecision {
|
|
186
|
+
/**
|
|
187
|
+
* 是否重试
|
|
188
|
+
*/
|
|
189
|
+
retry: boolean;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 延迟时间(毫秒)
|
|
193
|
+
*/
|
|
194
|
+
delay: number;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Redis 限流器选项
|
|
199
|
+
*/
|
|
200
|
+
export interface RedisRateLimiterOptions {
|
|
201
|
+
/**
|
|
202
|
+
* Redis 客户端(需要用户提供)
|
|
203
|
+
* 支持任何兼容的 Redis 客户端接口
|
|
204
|
+
*/
|
|
205
|
+
client: {
|
|
206
|
+
/**
|
|
207
|
+
* 获取键值
|
|
208
|
+
*/
|
|
209
|
+
get(key: string): Promise<string | null>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 设置键值(带过期时间)
|
|
213
|
+
*/
|
|
214
|
+
set(
|
|
215
|
+
key: string,
|
|
216
|
+
value: string,
|
|
217
|
+
options?: { PX?: number; EX?: number },
|
|
218
|
+
): Promise<void>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 删除键
|
|
222
|
+
*/
|
|
223
|
+
del(key: string): Promise<void>;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 递增键值(原子操作)
|
|
227
|
+
*/
|
|
228
|
+
incr(key: string): Promise<number>;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 设置键的过期时间
|
|
232
|
+
*/
|
|
233
|
+
expire(key: string, seconds: number): Promise<void>;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 检查键是否存在
|
|
237
|
+
*/
|
|
238
|
+
exists(key: string): Promise<number>;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* 键前缀
|
|
243
|
+
* @default 'ratelimit:'
|
|
244
|
+
*/
|
|
245
|
+
keyPrefix?: string;
|
|
246
|
+
}
|
|
247
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './config-center';
|
|
2
|
+
export * from './service-registry';
|
|
3
|
+
export * from './service-client';
|
|
4
|
+
export * from './governance';
|
|
5
|
+
export * from './tracing';
|
|
6
|
+
export * from './monitoring';
|
|
7
|
+
|
|
8
|
+
// 导出新增的装饰器
|
|
9
|
+
export { NacosValue } from './config-center';
|
|
10
|
+
export { ServiceDiscovery, type ServiceDiscoveryMetadata } from './service-registry';
|
|
11
|
+
export { ServiceCall, type ServiceCallMetadata } from './service-client';
|
|
12
|
+
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { ServiceCallMetrics, ServiceInstanceHealth, MonitoringOptions } from './types';
|
|
2
|
+
import { ServiceMetricsIntegration } from './metrics-integration';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 请求记录
|
|
6
|
+
*/
|
|
7
|
+
interface RequestRecord {
|
|
8
|
+
timestamp: number;
|
|
9
|
+
success: boolean;
|
|
10
|
+
latency: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 服务监控指标收集器
|
|
15
|
+
*/
|
|
16
|
+
export class ServiceMetricsCollector {
|
|
17
|
+
private readonly options: Required<MonitoringOptions>;
|
|
18
|
+
private readonly metrics: Map<string, ServiceCallMetrics> = new Map();
|
|
19
|
+
private readonly healthStatus: Map<string, ServiceInstanceHealth> = new Map();
|
|
20
|
+
private readonly requestRecords: Map<string, RequestRecord[]> = new Map();
|
|
21
|
+
private readonly maxRecordsPerInstance: number = 1000;
|
|
22
|
+
private metricsIntegration?: ServiceMetricsIntegration;
|
|
23
|
+
|
|
24
|
+
public constructor(options: MonitoringOptions = {}) {
|
|
25
|
+
this.options = {
|
|
26
|
+
enabled: options.enabled ?? true,
|
|
27
|
+
collectionInterval: options.collectionInterval ?? 60000,
|
|
28
|
+
autoReportToMetrics: options.autoReportToMetrics ?? true,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// 如果启用自动上报,创建集成器
|
|
32
|
+
if (this.options.autoReportToMetrics) {
|
|
33
|
+
this.metricsIntegration = new ServiceMetricsIntegration(this);
|
|
34
|
+
// 延迟启动,确保 MetricsModule 已注册
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
this.metricsIntegration?.start(this.options.collectionInterval);
|
|
37
|
+
}, 1000);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 记录服务调用
|
|
43
|
+
*/
|
|
44
|
+
public recordCall(
|
|
45
|
+
serviceName: string,
|
|
46
|
+
instance: string,
|
|
47
|
+
success: boolean,
|
|
48
|
+
latency: number,
|
|
49
|
+
): void {
|
|
50
|
+
if (!this.options.enabled) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const key = this.getKey(serviceName, instance);
|
|
55
|
+
|
|
56
|
+
// 记录请求
|
|
57
|
+
const records = this.requestRecords.get(key) ?? [];
|
|
58
|
+
records.push({
|
|
59
|
+
timestamp: Date.now(),
|
|
60
|
+
success,
|
|
61
|
+
latency,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// 限制记录数量
|
|
65
|
+
if (records.length > this.maxRecordsPerInstance) {
|
|
66
|
+
records.shift();
|
|
67
|
+
}
|
|
68
|
+
this.requestRecords.set(key, records);
|
|
69
|
+
|
|
70
|
+
// 更新指标
|
|
71
|
+
this.updateMetrics(serviceName, instance);
|
|
72
|
+
|
|
73
|
+
// 更新健康状态
|
|
74
|
+
this.updateHealthStatus(serviceName, instance, success);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 获取服务指标
|
|
79
|
+
*/
|
|
80
|
+
public getMetrics(serviceName: string, instance?: string): ServiceCallMetrics[] {
|
|
81
|
+
const result: ServiceCallMetrics[] = [];
|
|
82
|
+
|
|
83
|
+
for (const [key, metrics] of this.metrics.entries()) {
|
|
84
|
+
if (metrics.serviceName === serviceName && (!instance || metrics.instance === instance)) {
|
|
85
|
+
result.push(metrics);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 获取所有指标
|
|
94
|
+
*/
|
|
95
|
+
public getAllMetrics(): ServiceCallMetrics[] {
|
|
96
|
+
return Array.from(this.metrics.values());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 获取服务实例健康状态
|
|
101
|
+
*/
|
|
102
|
+
public getHealthStatus(serviceName: string, instance?: string): ServiceInstanceHealth[] {
|
|
103
|
+
const result: ServiceInstanceHealth[] = [];
|
|
104
|
+
|
|
105
|
+
for (const [key, health] of this.healthStatus.entries()) {
|
|
106
|
+
if (health.serviceName === serviceName && (!instance || health.instance === instance)) {
|
|
107
|
+
result.push(health);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 获取所有健康状态
|
|
116
|
+
*/
|
|
117
|
+
public getAllHealthStatus(): ServiceInstanceHealth[] {
|
|
118
|
+
return Array.from(this.healthStatus.values());
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 更新指标
|
|
123
|
+
*/
|
|
124
|
+
private updateMetrics(serviceName: string, instance: string): void {
|
|
125
|
+
const key = this.getKey(serviceName, instance);
|
|
126
|
+
const records = this.requestRecords.get(key) ?? [];
|
|
127
|
+
|
|
128
|
+
if (records.length === 0) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const totalRequests = records.length;
|
|
133
|
+
const successRequests = records.filter((r) => r.success).length;
|
|
134
|
+
const failureRequests = totalRequests - successRequests;
|
|
135
|
+
const errorRate = totalRequests > 0 ? failureRequests / totalRequests : 0;
|
|
136
|
+
|
|
137
|
+
const latencies = records.map((r) => r.latency);
|
|
138
|
+
const averageLatency = latencies.reduce((sum, l) => sum + l, 0) / latencies.length;
|
|
139
|
+
const minLatency = Math.min(...latencies);
|
|
140
|
+
const maxLatency = Math.max(...latencies);
|
|
141
|
+
|
|
142
|
+
const metrics: ServiceCallMetrics = {
|
|
143
|
+
serviceName,
|
|
144
|
+
instance,
|
|
145
|
+
totalRequests,
|
|
146
|
+
successRequests,
|
|
147
|
+
failureRequests,
|
|
148
|
+
averageLatency,
|
|
149
|
+
minLatency,
|
|
150
|
+
maxLatency,
|
|
151
|
+
errorRate,
|
|
152
|
+
lastUpdateTime: Date.now(),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
this.metrics.set(key, metrics);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 更新健康状态
|
|
160
|
+
*/
|
|
161
|
+
private updateHealthStatus(serviceName: string, instance: string, success: boolean): void {
|
|
162
|
+
const key = this.getKey(serviceName, instance);
|
|
163
|
+
const now = Date.now();
|
|
164
|
+
|
|
165
|
+
let health = this.healthStatus.get(key);
|
|
166
|
+
if (!health) {
|
|
167
|
+
health = {
|
|
168
|
+
serviceName,
|
|
169
|
+
instance,
|
|
170
|
+
healthy: true,
|
|
171
|
+
checkTime: now,
|
|
172
|
+
consecutiveFailures: 0,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (success) {
|
|
177
|
+
health.healthy = true;
|
|
178
|
+
health.consecutiveFailures = 0;
|
|
179
|
+
health.lastSuccessTime = now;
|
|
180
|
+
} else {
|
|
181
|
+
health.consecutiveFailures++;
|
|
182
|
+
health.lastFailureTime = now;
|
|
183
|
+
// 连续失败 3 次以上认为不健康
|
|
184
|
+
if (health.consecutiveFailures >= 3) {
|
|
185
|
+
health.healthy = false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
health.checkTime = now;
|
|
190
|
+
this.healthStatus.set(key, health);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 生成键
|
|
195
|
+
*/
|
|
196
|
+
private getKey(serviceName: string, instance: string): string {
|
|
197
|
+
return `${serviceName}:${instance}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 重置指标
|
|
202
|
+
*/
|
|
203
|
+
public reset(serviceName?: string, instance?: string): void {
|
|
204
|
+
if (serviceName && instance) {
|
|
205
|
+
const key = this.getKey(serviceName, instance);
|
|
206
|
+
this.metrics.delete(key);
|
|
207
|
+
this.healthStatus.delete(key);
|
|
208
|
+
this.requestRecords.delete(key);
|
|
209
|
+
} else {
|
|
210
|
+
this.metrics.clear();
|
|
211
|
+
this.healthStatus.clear();
|
|
212
|
+
this.requestRecords.clear();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 停止指标收集和上报
|
|
218
|
+
*/
|
|
219
|
+
public stop(): void {
|
|
220
|
+
this.metricsIntegration?.stop();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { MetricsCollector, METRICS_SERVICE_TOKEN } from '../../metrics';
|
|
2
|
+
import { ControllerRegistry } from '../../controller/controller';
|
|
3
|
+
import { ServiceMetricsCollector } from './metrics-collector';
|
|
4
|
+
import type { ServiceCallMetrics, ServiceInstanceHealth } from './types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 服务监控指标集成器
|
|
8
|
+
* 将 ServiceMetricsCollector 的指标自动上报到 MetricsModule
|
|
9
|
+
*/
|
|
10
|
+
export class ServiceMetricsIntegration {
|
|
11
|
+
private metricsCollector?: MetricsCollector;
|
|
12
|
+
private serviceMetricsCollector: ServiceMetricsCollector;
|
|
13
|
+
private updateInterval?: ReturnType<typeof setInterval>;
|
|
14
|
+
|
|
15
|
+
public constructor(serviceMetricsCollector: ServiceMetricsCollector) {
|
|
16
|
+
this.serviceMetricsCollector = serviceMetricsCollector;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 启动指标上报
|
|
21
|
+
*/
|
|
22
|
+
public start(intervalMs: number = 60000): void {
|
|
23
|
+
// 获取 MetricsCollector
|
|
24
|
+
try {
|
|
25
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
26
|
+
this.metricsCollector = container.resolve<MetricsCollector>(
|
|
27
|
+
METRICS_SERVICE_TOKEN,
|
|
28
|
+
);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.warn(
|
|
31
|
+
'[ServiceMetricsIntegration] MetricsCollector not found, skipping integration',
|
|
32
|
+
);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!this.metricsCollector) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 立即上报一次
|
|
41
|
+
this.reportMetrics();
|
|
42
|
+
|
|
43
|
+
// 定期上报
|
|
44
|
+
this.updateInterval = setInterval(() => {
|
|
45
|
+
this.reportMetrics();
|
|
46
|
+
}, intervalMs);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 停止指标上报
|
|
51
|
+
*/
|
|
52
|
+
public stop(): void {
|
|
53
|
+
if (this.updateInterval) {
|
|
54
|
+
clearInterval(this.updateInterval);
|
|
55
|
+
this.updateInterval = undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 上报指标到 MetricsCollector
|
|
61
|
+
*/
|
|
62
|
+
private reportMetrics(): void {
|
|
63
|
+
if (!this.metricsCollector) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const allMetrics = this.serviceMetricsCollector.getAllMetrics();
|
|
69
|
+
const allHealthStatus = this.serviceMetricsCollector.getAllHealthStatus();
|
|
70
|
+
|
|
71
|
+
// 上报服务调用指标
|
|
72
|
+
for (const metrics of allMetrics) {
|
|
73
|
+
const labels = {
|
|
74
|
+
service: metrics.serviceName,
|
|
75
|
+
instance: metrics.instance,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// 总请求数(counter)
|
|
79
|
+
this.metricsCollector.incrementCounter(
|
|
80
|
+
'service_calls_total',
|
|
81
|
+
labels,
|
|
82
|
+
metrics.totalRequests,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// 成功请求数(counter)
|
|
86
|
+
this.metricsCollector.incrementCounter(
|
|
87
|
+
'service_calls_success_total',
|
|
88
|
+
labels,
|
|
89
|
+
metrics.successRequests,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// 失败请求数(counter)
|
|
93
|
+
this.metricsCollector.incrementCounter(
|
|
94
|
+
'service_calls_failure_total',
|
|
95
|
+
labels,
|
|
96
|
+
metrics.failureRequests,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// 平均延迟(gauge)
|
|
100
|
+
this.metricsCollector.setGauge(
|
|
101
|
+
'service_call_latency_avg_ms',
|
|
102
|
+
labels,
|
|
103
|
+
metrics.averageLatency,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// 最小延迟(gauge)
|
|
107
|
+
this.metricsCollector.setGauge(
|
|
108
|
+
'service_call_latency_min_ms',
|
|
109
|
+
labels,
|
|
110
|
+
metrics.minLatency,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// 最大延迟(gauge)
|
|
114
|
+
this.metricsCollector.setGauge(
|
|
115
|
+
'service_call_latency_max_ms',
|
|
116
|
+
labels,
|
|
117
|
+
metrics.maxLatency,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// 错误率(gauge)
|
|
121
|
+
this.metricsCollector.setGauge(
|
|
122
|
+
'service_call_error_rate',
|
|
123
|
+
labels,
|
|
124
|
+
metrics.errorRate,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 上报健康状态指标
|
|
129
|
+
for (const health of allHealthStatus) {
|
|
130
|
+
const labels = {
|
|
131
|
+
service: health.serviceName,
|
|
132
|
+
instance: health.instance,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// 健康状态(gauge: 1=健康, 0=不健康)
|
|
136
|
+
this.metricsCollector.setGauge(
|
|
137
|
+
'service_instance_healthy',
|
|
138
|
+
labels,
|
|
139
|
+
health.healthy ? 1 : 0,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// 连续失败次数(gauge)
|
|
143
|
+
this.metricsCollector.setGauge(
|
|
144
|
+
'service_instance_consecutive_failures',
|
|
145
|
+
labels,
|
|
146
|
+
health.consecutiveFailures,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('[ServiceMetricsIntegration] Failed to report metrics:', error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|