@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,186 @@
|
|
|
1
|
+
import type { ServiceInstance } from '../service-registry/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 负载均衡策略类型
|
|
5
|
+
*/
|
|
6
|
+
export type LoadBalanceStrategy = 'random' | 'roundRobin' | 'weightedRoundRobin' | 'consistentHash' | 'leastActive';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 负载均衡器接口
|
|
10
|
+
*/
|
|
11
|
+
export interface LoadBalancer {
|
|
12
|
+
/**
|
|
13
|
+
* 选择服务实例
|
|
14
|
+
* @param instances - 服务实例列表
|
|
15
|
+
* @param key - 用于一致性哈希的键(可选)
|
|
16
|
+
* @returns 选中的服务实例
|
|
17
|
+
*/
|
|
18
|
+
select(instances: ServiceInstance[], key?: string): ServiceInstance | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 服务调用选项
|
|
23
|
+
*/
|
|
24
|
+
export interface ServiceCallOptions {
|
|
25
|
+
/**
|
|
26
|
+
* 服务名
|
|
27
|
+
*/
|
|
28
|
+
serviceName: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* HTTP 方法
|
|
32
|
+
*/
|
|
33
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 请求路径
|
|
37
|
+
*/
|
|
38
|
+
path: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 请求头(可选)
|
|
42
|
+
*/
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 请求体(可选)
|
|
47
|
+
*/
|
|
48
|
+
body?: string | object;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 查询参数(可选)
|
|
52
|
+
*/
|
|
53
|
+
query?: Record<string, string | number | boolean>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 超时时间(毫秒)
|
|
57
|
+
* @default 5000
|
|
58
|
+
*/
|
|
59
|
+
timeout?: number;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 重试次数
|
|
63
|
+
* @default 0
|
|
64
|
+
*/
|
|
65
|
+
retryCount?: number;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 重试延迟(毫秒)
|
|
69
|
+
* @default 1000
|
|
70
|
+
*/
|
|
71
|
+
retryDelay?: number;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 负载均衡策略
|
|
75
|
+
* @default 'roundRobin'
|
|
76
|
+
*/
|
|
77
|
+
loadBalanceStrategy?: LoadBalanceStrategy;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 用于一致性哈希的键(可选)
|
|
81
|
+
*/
|
|
82
|
+
consistentHashKey?: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 服务实例查询选项(可选)
|
|
86
|
+
*/
|
|
87
|
+
instanceOptions?: {
|
|
88
|
+
namespaceId?: string;
|
|
89
|
+
groupName?: string;
|
|
90
|
+
clusterName?: string;
|
|
91
|
+
healthyOnly?: boolean;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 是否启用熔断器(可选)
|
|
96
|
+
* @default false
|
|
97
|
+
*/
|
|
98
|
+
enableCircuitBreaker?: boolean;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 熔断器降级处理函数(可选)
|
|
102
|
+
*/
|
|
103
|
+
fallback?: () => Promise<unknown> | unknown;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 是否启用限流(可选)
|
|
107
|
+
* @default false
|
|
108
|
+
*/
|
|
109
|
+
enableRateLimit?: boolean;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 限流键(可选,默认使用服务名)
|
|
113
|
+
*/
|
|
114
|
+
rateLimitKey?: string;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 是否流式响应(用于 Server-Sent Events 等)
|
|
118
|
+
* @default false
|
|
119
|
+
*/
|
|
120
|
+
stream?: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 服务调用响应
|
|
125
|
+
*/
|
|
126
|
+
export interface ServiceCallResponse<T = unknown> {
|
|
127
|
+
/**
|
|
128
|
+
* HTTP 状态码
|
|
129
|
+
*/
|
|
130
|
+
status: number;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 响应头
|
|
134
|
+
*/
|
|
135
|
+
headers: Record<string, string>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 响应体
|
|
139
|
+
*/
|
|
140
|
+
data: T;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 使用的服务实例
|
|
144
|
+
*/
|
|
145
|
+
instance: ServiceInstance;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 服务请求拦截器接口
|
|
150
|
+
*/
|
|
151
|
+
export interface ServiceRequestInterceptor {
|
|
152
|
+
/**
|
|
153
|
+
* 拦截请求
|
|
154
|
+
* @param options - 服务调用选项
|
|
155
|
+
* @returns 修改后的选项或 Promise
|
|
156
|
+
*/
|
|
157
|
+
intercept(options: ServiceCallOptions): ServiceCallOptions | Promise<ServiceCallOptions>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 服务响应拦截器接口
|
|
162
|
+
*/
|
|
163
|
+
export interface ServiceResponseInterceptor {
|
|
164
|
+
/**
|
|
165
|
+
* 拦截响应
|
|
166
|
+
* @param response - 服务调用响应
|
|
167
|
+
* @returns 修改后的响应或 Promise
|
|
168
|
+
*/
|
|
169
|
+
intercept<T>(response: ServiceCallResponse<T>): ServiceCallResponse<T> | Promise<ServiceCallResponse<T>>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 服务调用错误
|
|
174
|
+
*/
|
|
175
|
+
export class ServiceCallError extends Error {
|
|
176
|
+
public constructor(
|
|
177
|
+
message: string,
|
|
178
|
+
public readonly status?: number,
|
|
179
|
+
public readonly response?: unknown,
|
|
180
|
+
public readonly instance?: ServiceInstance,
|
|
181
|
+
) {
|
|
182
|
+
super(message);
|
|
183
|
+
this.name = 'ServiceCallError';
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { ServiceRegistry, ServiceInstance } from './types';
|
|
3
|
+
import { SERVICE_REGISTRY_TOKEN } from './types';
|
|
4
|
+
import { ControllerRegistry } from '../../controller/controller';
|
|
5
|
+
import { ServiceRegistryHealthIntegration } from './health-integration';
|
|
6
|
+
import type { Constructor } from '../../core/types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 服务注册装饰器元数据键
|
|
10
|
+
*/
|
|
11
|
+
const SERVICE_REGISTRY_METADATA_KEY = Symbol('service-registry:metadata');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 服务注册元数据
|
|
15
|
+
*/
|
|
16
|
+
export interface ServiceRegistryMetadata {
|
|
17
|
+
/**
|
|
18
|
+
* 服务名
|
|
19
|
+
*/
|
|
20
|
+
serviceName: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 服务 IP(可选,默认从 Application 获取)
|
|
24
|
+
*/
|
|
25
|
+
ip?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 服务端口(可选,默认从 Application 获取)
|
|
29
|
+
*/
|
|
30
|
+
port?: number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 服务权重(可选)
|
|
34
|
+
*/
|
|
35
|
+
weight?: number;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 是否启用(可选)
|
|
39
|
+
*/
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 元数据(可选)
|
|
44
|
+
*/
|
|
45
|
+
metadata?: Record<string, string>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 集群名(可选)
|
|
49
|
+
*/
|
|
50
|
+
clusterName?: string;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 命名空间(可选)
|
|
54
|
+
*/
|
|
55
|
+
namespaceId?: string;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 分组名(可选)
|
|
59
|
+
*/
|
|
60
|
+
groupName?: string;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 是否健康(可选)
|
|
64
|
+
*/
|
|
65
|
+
healthy?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* ServiceRegistry 装饰器
|
|
70
|
+
* 用于自动注册服务到注册中心
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* @ServiceRegistry('my-service', { port: 3000 })
|
|
75
|
+
* @Controller('/api')
|
|
76
|
+
* class MyController {
|
|
77
|
+
* // 服务会在应用启动时自动注册
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function ServiceRegistry(
|
|
82
|
+
serviceName: string,
|
|
83
|
+
options?: {
|
|
84
|
+
ip?: string;
|
|
85
|
+
port?: number;
|
|
86
|
+
weight?: number;
|
|
87
|
+
enabled?: boolean;
|
|
88
|
+
metadata?: Record<string, string>;
|
|
89
|
+
clusterName?: string;
|
|
90
|
+
namespaceId?: string;
|
|
91
|
+
groupName?: string;
|
|
92
|
+
healthy?: boolean;
|
|
93
|
+
},
|
|
94
|
+
): ClassDecorator {
|
|
95
|
+
return function (target: Constructor<unknown> | Function) {
|
|
96
|
+
const targetClass = target as Constructor<unknown>;
|
|
97
|
+
const metadata: ServiceRegistryMetadata = {
|
|
98
|
+
serviceName,
|
|
99
|
+
ip: options?.ip,
|
|
100
|
+
port: options?.port,
|
|
101
|
+
weight: options?.weight,
|
|
102
|
+
enabled: options?.enabled,
|
|
103
|
+
metadata: options?.metadata,
|
|
104
|
+
clusterName: options?.clusterName,
|
|
105
|
+
namespaceId: options?.namespaceId,
|
|
106
|
+
groupName: options?.groupName,
|
|
107
|
+
healthy: options?.healthy ?? true,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
Reflect.defineMetadata(SERVICE_REGISTRY_METADATA_KEY, metadata, targetClass);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 获取服务注册元数据
|
|
116
|
+
*/
|
|
117
|
+
export function getServiceRegistryMetadata(
|
|
118
|
+
target: Constructor<unknown>,
|
|
119
|
+
): ServiceRegistryMetadata | undefined {
|
|
120
|
+
return Reflect.getMetadata(SERVICE_REGISTRY_METADATA_KEY, target);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 全局健康检查集成器实例(每个应用一个)
|
|
124
|
+
let healthIntegration: ServiceRegistryHealthIntegration | undefined;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 注册服务实例
|
|
128
|
+
*/
|
|
129
|
+
export async function registerServiceInstance(
|
|
130
|
+
target: Constructor<unknown>,
|
|
131
|
+
appPort?: number,
|
|
132
|
+
appHost?: string,
|
|
133
|
+
): Promise<void> {
|
|
134
|
+
const metadata = getServiceRegistryMetadata(target);
|
|
135
|
+
if (!metadata) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
140
|
+
const serviceRegistry = container.resolve<ServiceRegistry>(
|
|
141
|
+
SERVICE_REGISTRY_TOKEN,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (!serviceRegistry) {
|
|
145
|
+
console.warn(
|
|
146
|
+
`[ServiceRegistry] ServiceRegistry not found, skipping registration for ${metadata.serviceName}`,
|
|
147
|
+
);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const instance: ServiceInstance = {
|
|
152
|
+
serviceName: metadata.serviceName,
|
|
153
|
+
ip: metadata.ip ?? appHost ?? '127.0.0.1',
|
|
154
|
+
port: metadata.port ?? appPort ?? 3000,
|
|
155
|
+
weight: metadata.weight,
|
|
156
|
+
enabled: metadata.enabled ?? true,
|
|
157
|
+
metadata: metadata.metadata,
|
|
158
|
+
clusterName: metadata.clusterName,
|
|
159
|
+
namespaceId: metadata.namespaceId,
|
|
160
|
+
groupName: metadata.groupName,
|
|
161
|
+
healthy: metadata.healthy ?? true,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// 如果启用了健康检查集成,使用集成器注册
|
|
166
|
+
const hasHealthModule = container.isRegistered(
|
|
167
|
+
Symbol('@dangao/bun-server:health:indicators'),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
if (hasHealthModule) {
|
|
171
|
+
if (!healthIntegration) {
|
|
172
|
+
healthIntegration = new ServiceRegistryHealthIntegration();
|
|
173
|
+
}
|
|
174
|
+
await healthIntegration.registerWithHealthCheck(instance);
|
|
175
|
+
} else {
|
|
176
|
+
// 普通注册
|
|
177
|
+
await serviceRegistry.register(instance);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(
|
|
181
|
+
`[ServiceRegistry] Service ${metadata.serviceName} registered successfully at ${instance.ip}:${instance.port}`,
|
|
182
|
+
);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error(
|
|
185
|
+
`[ServiceRegistry] Failed to register service ${metadata.serviceName}:`,
|
|
186
|
+
error,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 注销服务实例
|
|
193
|
+
*/
|
|
194
|
+
export async function deregisterServiceInstance(
|
|
195
|
+
target: Constructor<unknown>,
|
|
196
|
+
appPort?: number,
|
|
197
|
+
appHost?: string,
|
|
198
|
+
): Promise<void> {
|
|
199
|
+
const metadata = getServiceRegistryMetadata(target);
|
|
200
|
+
if (!metadata) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
205
|
+
const serviceRegistry = container.resolve<ServiceRegistry>(
|
|
206
|
+
SERVICE_REGISTRY_TOKEN,
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (!serviceRegistry) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const instance: ServiceInstance = {
|
|
214
|
+
serviceName: metadata.serviceName,
|
|
215
|
+
ip: metadata.ip ?? appHost ?? '127.0.0.1',
|
|
216
|
+
port: metadata.port ?? appPort ?? 3000,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
await serviceRegistry.deregister(instance);
|
|
221
|
+
|
|
222
|
+
// 停止健康检查更新(如果是最后一个实例)
|
|
223
|
+
if (healthIntegration) {
|
|
224
|
+
healthIntegration.stop();
|
|
225
|
+
healthIntegration = undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
console.log(
|
|
229
|
+
`[ServiceRegistry] Service ${metadata.serviceName} deregistered successfully`,
|
|
230
|
+
);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error(
|
|
233
|
+
`[ServiceRegistry] Failed to deregister service ${metadata.serviceName}:`,
|
|
234
|
+
error,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { ServiceRegistry, ServiceInstance } from './types';
|
|
3
|
+
import { SERVICE_REGISTRY_TOKEN } from './types';
|
|
4
|
+
import { ControllerRegistry } from '../../controller/controller';
|
|
5
|
+
import type { Constructor } from '../../core/types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 服务发现装饰器元数据键
|
|
9
|
+
*/
|
|
10
|
+
const SERVICE_DISCOVERY_METADATA_KEY = Symbol('service-discovery:metadata');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 服务发现元数据
|
|
14
|
+
*/
|
|
15
|
+
export interface ServiceDiscoveryMetadata {
|
|
16
|
+
/**
|
|
17
|
+
* 服务名
|
|
18
|
+
*/
|
|
19
|
+
serviceName: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 服务实例查询选项
|
|
23
|
+
*/
|
|
24
|
+
options?: {
|
|
25
|
+
namespaceId?: string;
|
|
26
|
+
groupName?: string;
|
|
27
|
+
clusterName?: string;
|
|
28
|
+
healthyOnly?: boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 属性名(用于注入服务实例列表)
|
|
33
|
+
*/
|
|
34
|
+
propertyKey?: string | symbol;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* ServiceDiscovery 装饰器
|
|
39
|
+
* 用于自动注入服务实例列表
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* @Injectable()
|
|
44
|
+
* class MyService {
|
|
45
|
+
* @ServiceDiscovery('my-service')
|
|
46
|
+
* public instances: ServiceInstance[] = [];
|
|
47
|
+
*
|
|
48
|
+
* public async getServiceInstances() {
|
|
49
|
+
* // instances 会自动更新
|
|
50
|
+
* return this.instances;
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function ServiceDiscovery(
|
|
56
|
+
serviceName: string,
|
|
57
|
+
options?: {
|
|
58
|
+
namespaceId?: string;
|
|
59
|
+
groupName?: string;
|
|
60
|
+
clusterName?: string;
|
|
61
|
+
healthyOnly?: boolean;
|
|
62
|
+
},
|
|
63
|
+
): PropertyDecorator {
|
|
64
|
+
return function (target: object, propertyKey: string | symbol) {
|
|
65
|
+
const metadata: ServiceDiscoveryMetadata = {
|
|
66
|
+
serviceName,
|
|
67
|
+
options,
|
|
68
|
+
propertyKey,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// 保存元数据
|
|
72
|
+
const existingMetadata: Map<string | symbol, ServiceDiscoveryMetadata> =
|
|
73
|
+
Reflect.getMetadata(SERVICE_DISCOVERY_METADATA_KEY, target.constructor) ||
|
|
74
|
+
new Map();
|
|
75
|
+
existingMetadata.set(propertyKey, metadata);
|
|
76
|
+
Reflect.defineMetadata(
|
|
77
|
+
SERVICE_DISCOVERY_METADATA_KEY,
|
|
78
|
+
existingMetadata,
|
|
79
|
+
target.constructor,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// 定义属性描述符,实现自动注入
|
|
83
|
+
// 注意:属性 getter 必须是同步的,不能是 async
|
|
84
|
+
// 实际值通过初始化函数在实例化时预加载
|
|
85
|
+
let instances: ServiceInstance[] = [];
|
|
86
|
+
|
|
87
|
+
Object.defineProperty(target, propertyKey, {
|
|
88
|
+
get: () => {
|
|
89
|
+
// 同步返回已加载的实例列表(如果还未加载,返回空数组)
|
|
90
|
+
return instances;
|
|
91
|
+
},
|
|
92
|
+
set: (newInstances: ServiceInstance[]) => {
|
|
93
|
+
instances = newInstances;
|
|
94
|
+
},
|
|
95
|
+
enumerable: true,
|
|
96
|
+
configurable: true,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 获取类的所有服务发现元数据
|
|
103
|
+
*/
|
|
104
|
+
export function getServiceDiscoveryMetadata(
|
|
105
|
+
target: Constructor<unknown>,
|
|
106
|
+
): Map<string | symbol, ServiceDiscoveryMetadata> {
|
|
107
|
+
return (
|
|
108
|
+
Reflect.getMetadata(SERVICE_DISCOVERY_METADATA_KEY, target) || new Map()
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 初始化类的服务发现属性
|
|
114
|
+
* 在类实例化后调用,自动加载服务实例列表
|
|
115
|
+
*/
|
|
116
|
+
export async function initializeServiceDiscovery(
|
|
117
|
+
instance: object,
|
|
118
|
+
target: Constructor<unknown>,
|
|
119
|
+
): Promise<void> {
|
|
120
|
+
const metadata = getServiceDiscoveryMetadata(target);
|
|
121
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
122
|
+
const serviceRegistry = container.resolve<ServiceRegistry>(
|
|
123
|
+
SERVICE_REGISTRY_TOKEN,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (!serviceRegistry) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const [propertyKey, meta] of metadata.entries()) {
|
|
131
|
+
try {
|
|
132
|
+
const instances = await serviceRegistry.getInstances(meta.serviceName, {
|
|
133
|
+
namespaceId: meta.options?.namespaceId,
|
|
134
|
+
groupName: meta.options?.groupName,
|
|
135
|
+
clusterName: meta.options?.clusterName,
|
|
136
|
+
healthyOnly: meta.options?.healthyOnly ?? true,
|
|
137
|
+
});
|
|
138
|
+
(instance as any)[propertyKey] = instances;
|
|
139
|
+
|
|
140
|
+
// 设置监听器以自动更新实例列表
|
|
141
|
+
serviceRegistry.watchInstances(
|
|
142
|
+
meta.serviceName,
|
|
143
|
+
(newInstances) => {
|
|
144
|
+
(instance as any)[propertyKey] = newInstances;
|
|
145
|
+
},
|
|
146
|
+
meta.options,
|
|
147
|
+
);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(
|
|
150
|
+
`[ServiceDiscovery] Failed to initialize instances for ${meta.serviceName}:`,
|
|
151
|
+
error,
|
|
152
|
+
);
|
|
153
|
+
(instance as any)[propertyKey] = [];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { ServiceRegistry, ServiceInstance } from './types';
|
|
2
|
+
import { SERVICE_REGISTRY_TOKEN } from './types';
|
|
3
|
+
import { HEALTH_INDICATORS_TOKEN } from '../../health/types';
|
|
4
|
+
import type { HealthIndicator } from '../../health/types';
|
|
5
|
+
import { ControllerRegistry } from '../../controller/controller';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 健康检查与服务注册集成器
|
|
9
|
+
* 根据健康检查状态自动更新服务注册的健康状态
|
|
10
|
+
*/
|
|
11
|
+
export class ServiceRegistryHealthIntegration {
|
|
12
|
+
private serviceRegistry?: ServiceRegistry;
|
|
13
|
+
private healthIndicators: HealthIndicator[] = [];
|
|
14
|
+
private updateInterval?: ReturnType<typeof setInterval>;
|
|
15
|
+
private registeredInstances: Map<string, ServiceInstance> = new Map();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 注册服务实例并启动健康检查集成
|
|
19
|
+
*/
|
|
20
|
+
public async registerWithHealthCheck(
|
|
21
|
+
instance: ServiceInstance,
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
// 保存实例信息
|
|
24
|
+
this.registeredInstances.set(
|
|
25
|
+
`${instance.serviceName}:${instance.ip}:${instance.port}`,
|
|
26
|
+
instance,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// 获取 ServiceRegistry
|
|
30
|
+
try {
|
|
31
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
32
|
+
this.serviceRegistry = container.resolve<ServiceRegistry>(
|
|
33
|
+
SERVICE_REGISTRY_TOKEN,
|
|
34
|
+
);
|
|
35
|
+
this.healthIndicators = container.resolve<HealthIndicator[]>(
|
|
36
|
+
HEALTH_INDICATORS_TOKEN,
|
|
37
|
+
) || [];
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.warn(
|
|
40
|
+
'[ServiceRegistryHealthIntegration] ServiceRegistry or HealthIndicators not found',
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!this.serviceRegistry) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 初始注册
|
|
49
|
+
await this.serviceRegistry.register(instance);
|
|
50
|
+
|
|
51
|
+
// 启动定期健康检查更新
|
|
52
|
+
this.startHealthCheckUpdates(instance);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 启动健康检查更新
|
|
57
|
+
*/
|
|
58
|
+
private startHealthCheckUpdates(instance: ServiceInstance): void {
|
|
59
|
+
// 如果已经有更新任务在运行,不重复启动
|
|
60
|
+
if (this.updateInterval) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 定期更新健康状态(每 30 秒)
|
|
65
|
+
this.updateInterval = setInterval(async () => {
|
|
66
|
+
await this.updateHealthStatus();
|
|
67
|
+
}, 30000);
|
|
68
|
+
|
|
69
|
+
// 立即执行一次
|
|
70
|
+
this.updateHealthStatus().catch((error) => {
|
|
71
|
+
console.error('[ServiceRegistryHealthIntegration] Failed to update health status:', error);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 更新所有注册服务的健康状态
|
|
77
|
+
*/
|
|
78
|
+
private async updateHealthStatus(): Promise<void> {
|
|
79
|
+
if (!this.serviceRegistry) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 检查健康状态
|
|
84
|
+
const isHealthy = await this.checkHealth();
|
|
85
|
+
|
|
86
|
+
// 更新所有注册实例的健康状态
|
|
87
|
+
for (const instance of this.registeredInstances.values()) {
|
|
88
|
+
if (instance.healthy !== isHealthy) {
|
|
89
|
+
try {
|
|
90
|
+
// 更新实例的健康状态
|
|
91
|
+
const updatedInstance: ServiceInstance = {
|
|
92
|
+
...instance,
|
|
93
|
+
healthy: isHealthy,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// 通过续约更新健康状态
|
|
97
|
+
await this.serviceRegistry.renew(updatedInstance);
|
|
98
|
+
|
|
99
|
+
console.log(
|
|
100
|
+
`[ServiceRegistryHealthIntegration] Updated health status for ${instance.serviceName} to ${isHealthy ? 'healthy' : 'unhealthy'}`,
|
|
101
|
+
);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error(
|
|
104
|
+
`[ServiceRegistryHealthIntegration] Failed to update health status for ${instance.serviceName}:`,
|
|
105
|
+
error,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 检查应用健康状态
|
|
114
|
+
*/
|
|
115
|
+
private async checkHealth(): Promise<boolean> {
|
|
116
|
+
if (this.healthIndicators.length === 0) {
|
|
117
|
+
// 如果没有健康检查指示器,默认认为健康
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
// 检查所有健康指示器
|
|
123
|
+
for (const indicator of this.healthIndicators) {
|
|
124
|
+
const result = await Promise.resolve(indicator.check());
|
|
125
|
+
if (result.status !== 'up') {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error('[ServiceRegistryHealthIntegration] Health check failed:', error);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 停止健康检查更新
|
|
138
|
+
*/
|
|
139
|
+
public stop(): void {
|
|
140
|
+
if (this.updateInterval) {
|
|
141
|
+
clearInterval(this.updateInterval);
|
|
142
|
+
this.updateInterval = undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|