@hazeljs/discovery 0.2.0-alpha.1
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/LICENSE +192 -0
- package/README.md +450 -0
- package/dist/__tests__/consul-backend.test.d.ts +6 -0
- package/dist/__tests__/consul-backend.test.d.ts.map +1 -0
- package/dist/__tests__/consul-backend.test.js +300 -0
- package/dist/__tests__/decorators.test.d.ts +5 -0
- package/dist/__tests__/decorators.test.d.ts.map +1 -0
- package/dist/__tests__/decorators.test.js +72 -0
- package/dist/__tests__/discovery-client.test.d.ts +5 -0
- package/dist/__tests__/discovery-client.test.d.ts.map +1 -0
- package/dist/__tests__/discovery-client.test.js +142 -0
- package/dist/__tests__/kubernetes-backend.test.d.ts +6 -0
- package/dist/__tests__/kubernetes-backend.test.d.ts.map +1 -0
- package/dist/__tests__/kubernetes-backend.test.js +261 -0
- package/dist/__tests__/load-balancer-strategies.test.d.ts +5 -0
- package/dist/__tests__/load-balancer-strategies.test.d.ts.map +1 -0
- package/dist/__tests__/load-balancer-strategies.test.js +234 -0
- package/dist/__tests__/memory-backend.test.d.ts +5 -0
- package/dist/__tests__/memory-backend.test.d.ts.map +1 -0
- package/dist/__tests__/memory-backend.test.js +246 -0
- package/dist/__tests__/redis-backend.test.d.ts +6 -0
- package/dist/__tests__/redis-backend.test.d.ts.map +1 -0
- package/dist/__tests__/redis-backend.test.js +280 -0
- package/dist/__tests__/service-client.test.d.ts +5 -0
- package/dist/__tests__/service-client.test.d.ts.map +1 -0
- package/dist/__tests__/service-client.test.js +216 -0
- package/dist/__tests__/service-registry.test.d.ts +5 -0
- package/dist/__tests__/service-registry.test.d.ts.map +1 -0
- package/dist/__tests__/service-registry.test.js +65 -0
- package/dist/backends/consul-backend.d.ts +115 -0
- package/dist/backends/consul-backend.d.ts.map +1 -0
- package/dist/backends/consul-backend.js +259 -0
- package/dist/backends/kubernetes-backend.d.ts +103 -0
- package/dist/backends/kubernetes-backend.d.ts.map +1 -0
- package/dist/backends/kubernetes-backend.js +153 -0
- package/dist/backends/memory-backend.d.ts +21 -0
- package/dist/backends/memory-backend.d.ts.map +1 -0
- package/dist/backends/memory-backend.js +86 -0
- package/dist/backends/redis-backend.d.ts +76 -0
- package/dist/backends/redis-backend.d.ts.map +1 -0
- package/dist/backends/redis-backend.js +220 -0
- package/dist/backends/registry-backend.d.ts +39 -0
- package/dist/backends/registry-backend.d.ts.map +1 -0
- package/dist/backends/registry-backend.js +5 -0
- package/dist/client/discovery-client.d.ts +49 -0
- package/dist/client/discovery-client.d.ts.map +1 -0
- package/dist/client/discovery-client.js +123 -0
- package/dist/client/service-client.d.ts +48 -0
- package/dist/client/service-client.d.ts.map +1 -0
- package/dist/client/service-client.js +155 -0
- package/dist/decorators/inject-service-client.decorator.d.ts +16 -0
- package/dist/decorators/inject-service-client.decorator.d.ts.map +1 -0
- package/dist/decorators/inject-service-client.decorator.js +24 -0
- package/dist/decorators/service-registry.decorator.d.ts +11 -0
- package/dist/decorators/service-registry.decorator.d.ts.map +1 -0
- package/dist/decorators/service-registry.decorator.js +20 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/load-balancer/strategies.d.ts +82 -0
- package/dist/load-balancer/strategies.d.ts.map +1 -0
- package/dist/load-balancer/strategies.js +209 -0
- package/dist/registry/service-registry.d.ts +51 -0
- package/dist/registry/service-registry.d.ts.map +1 -0
- package/dist/registry/service-registry.js +159 -0
- package/dist/types/index.d.ts +61 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +14 -0
- package/dist/utils/filter.d.ts +10 -0
- package/dist/utils/filter.d.ts.map +1 -0
- package/dist/utils/filter.js +39 -0
- package/dist/utils/logger.d.ts +21 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +34 -0
- package/dist/utils/validation.d.ts +36 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +109 -0
- package/package.json +81 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kubernetes Registry Backend
|
|
3
|
+
* Uses Kubernetes Service Discovery
|
|
4
|
+
*/
|
|
5
|
+
import { RegistryBackend } from './registry-backend';
|
|
6
|
+
import { ServiceInstance, ServiceFilter } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Minimal type definitions for the Kubernetes client API surface we use.
|
|
9
|
+
* These mirror the shapes exposed by `@kubernetes/client-node`.
|
|
10
|
+
*/
|
|
11
|
+
export interface KubeConfig {
|
|
12
|
+
makeApiClient(apiClass: new () => any): any;
|
|
13
|
+
}
|
|
14
|
+
export interface K8sEndpointAddress {
|
|
15
|
+
ip: string;
|
|
16
|
+
targetRef?: {
|
|
17
|
+
name?: string;
|
|
18
|
+
};
|
|
19
|
+
nodeName?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface K8sEndpointPort {
|
|
22
|
+
port: number;
|
|
23
|
+
}
|
|
24
|
+
export interface K8sEndpointSubset {
|
|
25
|
+
ports?: K8sEndpointPort[];
|
|
26
|
+
addresses?: K8sEndpointAddress[];
|
|
27
|
+
notReadyAddresses?: K8sEndpointAddress[];
|
|
28
|
+
}
|
|
29
|
+
export interface K8sObjectMeta {
|
|
30
|
+
name?: string;
|
|
31
|
+
annotations?: Record<string, string>;
|
|
32
|
+
labels?: Record<string, string>;
|
|
33
|
+
creationTimestamp?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface K8sEndpoints {
|
|
36
|
+
metadata?: K8sObjectMeta;
|
|
37
|
+
subsets?: K8sEndpointSubset[];
|
|
38
|
+
}
|
|
39
|
+
export interface K8sService {
|
|
40
|
+
metadata?: K8sObjectMeta;
|
|
41
|
+
}
|
|
42
|
+
export interface K8sApiResponse<T> {
|
|
43
|
+
body: T;
|
|
44
|
+
}
|
|
45
|
+
export interface CoreV1ApiLike {
|
|
46
|
+
readNamespacedEndpoints(name: string, namespace: string): Promise<K8sApiResponse<K8sEndpoints>>;
|
|
47
|
+
listNamespacedService(namespace: string, pretty?: string, allowWatchBookmarks?: boolean, _continue?: string, fieldSelector?: string, labelSelector?: string): Promise<K8sApiResponse<{
|
|
48
|
+
items: K8sService[];
|
|
49
|
+
}>>;
|
|
50
|
+
}
|
|
51
|
+
export interface KubernetesBackendConfig {
|
|
52
|
+
namespace?: string;
|
|
53
|
+
labelSelector?: string;
|
|
54
|
+
}
|
|
55
|
+
export declare class KubernetesRegistryBackend implements RegistryBackend {
|
|
56
|
+
private k8sApi;
|
|
57
|
+
private readonly namespace;
|
|
58
|
+
private readonly labelSelector;
|
|
59
|
+
constructor(kubeConfig: KubeConfig, config?: KubernetesBackendConfig);
|
|
60
|
+
/**
|
|
61
|
+
* Register a service instance
|
|
62
|
+
* In Kubernetes, services are registered via Service/Endpoints objects
|
|
63
|
+
* This is typically handled by K8s itself
|
|
64
|
+
*/
|
|
65
|
+
register(_instance: ServiceInstance): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Deregister a service instance
|
|
68
|
+
* In Kubernetes, this is handled automatically when pods terminate
|
|
69
|
+
*/
|
|
70
|
+
deregister(_instanceId: string): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Update service instance heartbeat
|
|
73
|
+
* Kubernetes handles liveness/readiness probes
|
|
74
|
+
*/
|
|
75
|
+
heartbeat(_instanceId: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Get all instances of a service from Kubernetes Endpoints
|
|
78
|
+
*/
|
|
79
|
+
getInstances(serviceName: string, filter?: ServiceFilter): Promise<ServiceInstance[]>;
|
|
80
|
+
/**
|
|
81
|
+
* Get a specific service instance
|
|
82
|
+
*/
|
|
83
|
+
getInstance(instanceId: string): Promise<ServiceInstance | null>;
|
|
84
|
+
/**
|
|
85
|
+
* Get all registered services in the namespace
|
|
86
|
+
*/
|
|
87
|
+
getAllServices(): Promise<string[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Update service instance status
|
|
90
|
+
* In Kubernetes, status is managed by readiness probes
|
|
91
|
+
*/
|
|
92
|
+
updateStatus(_instanceId: string, _status: string): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Clean up expired instances
|
|
95
|
+
* Kubernetes handles this automatically
|
|
96
|
+
*/
|
|
97
|
+
cleanup(): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Create a ServiceInstance from Kubernetes endpoint data
|
|
100
|
+
*/
|
|
101
|
+
private createServiceInstance;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=kubernetes-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kubernetes-backend.d.ts","sourceRoot":"","sources":["../../src/backends/kubernetes-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Kubernetes Registry Backend
|
|
4
|
+
* Uses Kubernetes Service Discovery
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.KubernetesRegistryBackend = void 0;
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const filter_1 = require("../utils/filter");
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
11
|
+
const validation_1 = require("../utils/validation");
|
|
12
|
+
class KubernetesRegistryBackend {
|
|
13
|
+
constructor(kubeConfig, config = {}) {
|
|
14
|
+
(0, validation_1.validateKubernetesBackendConfig)(config);
|
|
15
|
+
// Import CoreV1Api dynamically to avoid build errors when @kubernetes/client-node is not installed
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
17
|
+
const { CoreV1Api: CoreV1ApiClass } = require('@kubernetes/client-node');
|
|
18
|
+
this.k8sApi = kubeConfig.makeApiClient(CoreV1ApiClass);
|
|
19
|
+
this.namespace = config.namespace || 'default';
|
|
20
|
+
this.labelSelector = config.labelSelector || 'app.kubernetes.io/managed-by=hazeljs';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register a service instance
|
|
24
|
+
* In Kubernetes, services are registered via Service/Endpoints objects
|
|
25
|
+
* This is typically handled by K8s itself
|
|
26
|
+
*/
|
|
27
|
+
async register(_instance) {
|
|
28
|
+
// In Kubernetes, service registration is handled by the platform
|
|
29
|
+
// We can optionally create/update annotations on the pod
|
|
30
|
+
// Service registration is managed by Kubernetes
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Deregister a service instance
|
|
34
|
+
* In Kubernetes, this is handled automatically when pods terminate
|
|
35
|
+
*/
|
|
36
|
+
async deregister(_instanceId) {
|
|
37
|
+
// Service deregistration is managed by Kubernetes
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Update service instance heartbeat
|
|
41
|
+
* Kubernetes handles liveness/readiness probes
|
|
42
|
+
*/
|
|
43
|
+
async heartbeat(_instanceId) {
|
|
44
|
+
// Kubernetes handles health checks via probes
|
|
45
|
+
// No manual heartbeat needed
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get all instances of a service from Kubernetes Endpoints
|
|
49
|
+
*/
|
|
50
|
+
async getInstances(serviceName, filter) {
|
|
51
|
+
const logger = logger_1.DiscoveryLogger.getLogger();
|
|
52
|
+
try {
|
|
53
|
+
// Get service endpoints
|
|
54
|
+
const endpointsResponse = await this.k8sApi.readNamespacedEndpoints(serviceName, this.namespace);
|
|
55
|
+
const endpoints = endpointsResponse.body;
|
|
56
|
+
const instances = [];
|
|
57
|
+
if (!endpoints.subsets) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
// Process each subset
|
|
61
|
+
for (const subset of endpoints.subsets) {
|
|
62
|
+
const ports = subset.ports || [];
|
|
63
|
+
const addresses = subset.addresses || [];
|
|
64
|
+
const notReadyAddresses = subset.notReadyAddresses || [];
|
|
65
|
+
// Process ready addresses
|
|
66
|
+
for (const address of addresses) {
|
|
67
|
+
for (const port of ports) {
|
|
68
|
+
const instance = this.createServiceInstance(serviceName, address, port, types_1.ServiceStatus.UP, endpoints.metadata);
|
|
69
|
+
instances.push(instance);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Process not-ready addresses
|
|
73
|
+
for (const address of notReadyAddresses) {
|
|
74
|
+
for (const port of ports) {
|
|
75
|
+
const instance = this.createServiceInstance(serviceName, address, port, types_1.ServiceStatus.STARTING, endpoints.metadata);
|
|
76
|
+
instances.push(instance);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Apply filters
|
|
81
|
+
return (0, filter_1.applyServiceFilter)(instances, filter);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger.error(`Failed to get instances for service "${serviceName}" from Kubernetes`, error);
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get a specific service instance
|
|
90
|
+
*/
|
|
91
|
+
async getInstance(instanceId) {
|
|
92
|
+
// Parse instanceId to get service name and address
|
|
93
|
+
const [serviceName] = instanceId.split(':');
|
|
94
|
+
const instances = await this.getInstances(serviceName);
|
|
95
|
+
return instances.find((i) => i.id === instanceId) || null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get all registered services in the namespace
|
|
99
|
+
*/
|
|
100
|
+
async getAllServices() {
|
|
101
|
+
const logger = logger_1.DiscoveryLogger.getLogger();
|
|
102
|
+
try {
|
|
103
|
+
const servicesResponse = await this.k8sApi.listNamespacedService(this.namespace, undefined, undefined, undefined, undefined, this.labelSelector);
|
|
104
|
+
return servicesResponse.body.items.map((service) => service.metadata?.name || '');
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
logger.error('Failed to list services from Kubernetes', error);
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Update service instance status
|
|
113
|
+
* In Kubernetes, status is managed by readiness probes
|
|
114
|
+
*/
|
|
115
|
+
async updateStatus(_instanceId, _status) {
|
|
116
|
+
// Kubernetes manages status via probes
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Clean up expired instances
|
|
120
|
+
* Kubernetes handles this automatically
|
|
121
|
+
*/
|
|
122
|
+
async cleanup() {
|
|
123
|
+
// Kubernetes automatically removes terminated pods
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Create a ServiceInstance from Kubernetes endpoint data
|
|
127
|
+
*/
|
|
128
|
+
createServiceInstance(serviceName, address, port, status, metadata) {
|
|
129
|
+
const host = address.ip;
|
|
130
|
+
const portNumber = port.port;
|
|
131
|
+
const instanceId = `${serviceName}:${host}:${portNumber}`;
|
|
132
|
+
// Extract metadata from annotations and labels
|
|
133
|
+
const annotations = metadata?.annotations || {};
|
|
134
|
+
const labels = metadata?.labels || {};
|
|
135
|
+
return {
|
|
136
|
+
id: instanceId,
|
|
137
|
+
name: serviceName,
|
|
138
|
+
host,
|
|
139
|
+
port: portNumber,
|
|
140
|
+
status,
|
|
141
|
+
metadata: {
|
|
142
|
+
...annotations,
|
|
143
|
+
podName: address.targetRef?.name,
|
|
144
|
+
nodeName: address.nodeName,
|
|
145
|
+
},
|
|
146
|
+
tags: Object.keys(labels),
|
|
147
|
+
zone: labels['topology.kubernetes.io/zone'] || labels['failure-domain.beta.kubernetes.io/zone'],
|
|
148
|
+
lastHeartbeat: new Date(),
|
|
149
|
+
registeredAt: new Date(metadata?.creationTimestamp || Date.now()),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.KubernetesRegistryBackend = KubernetesRegistryBackend;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-Memory Registry Backend
|
|
3
|
+
* For development and testing
|
|
4
|
+
*/
|
|
5
|
+
import { RegistryBackend } from './registry-backend';
|
|
6
|
+
import { ServiceInstance, ServiceFilter } from '../types';
|
|
7
|
+
export declare class MemoryRegistryBackend implements RegistryBackend {
|
|
8
|
+
private instances;
|
|
9
|
+
private serviceIndex;
|
|
10
|
+
private readonly expirationTime;
|
|
11
|
+
constructor(expirationTime?: number);
|
|
12
|
+
register(instance: ServiceInstance): Promise<void>;
|
|
13
|
+
deregister(instanceId: string): Promise<void>;
|
|
14
|
+
heartbeat(instanceId: string): Promise<void>;
|
|
15
|
+
getInstances(serviceName: string, filter?: ServiceFilter): Promise<ServiceInstance[]>;
|
|
16
|
+
getInstance(instanceId: string): Promise<ServiceInstance | null>;
|
|
17
|
+
getAllServices(): Promise<string[]>;
|
|
18
|
+
updateStatus(instanceId: string, status: string): Promise<void>;
|
|
19
|
+
cleanup(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=memory-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-backend.d.ts","sourceRoot":"","sources":["../../src/backends/memory-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAC;AAGzE,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,cAAc,SAAQ;IAK5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlD,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5C,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAgBrF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAIhE,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAInC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAe/B"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* In-Memory Registry Backend
|
|
4
|
+
* For development and testing
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MemoryRegistryBackend = void 0;
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const filter_1 = require("../utils/filter");
|
|
10
|
+
class MemoryRegistryBackend {
|
|
11
|
+
constructor(expirationTime = 90000) {
|
|
12
|
+
this.instances = new Map();
|
|
13
|
+
this.serviceIndex = new Map();
|
|
14
|
+
// 90 seconds default
|
|
15
|
+
this.expirationTime = expirationTime;
|
|
16
|
+
}
|
|
17
|
+
async register(instance) {
|
|
18
|
+
this.instances.set(instance.id, instance);
|
|
19
|
+
// Update service index
|
|
20
|
+
if (!this.serviceIndex.has(instance.name)) {
|
|
21
|
+
this.serviceIndex.set(instance.name, new Set());
|
|
22
|
+
}
|
|
23
|
+
this.serviceIndex.get(instance.name).add(instance.id);
|
|
24
|
+
}
|
|
25
|
+
async deregister(instanceId) {
|
|
26
|
+
const instance = this.instances.get(instanceId);
|
|
27
|
+
if (instance) {
|
|
28
|
+
this.instances.delete(instanceId);
|
|
29
|
+
// Update service index
|
|
30
|
+
const serviceInstances = this.serviceIndex.get(instance.name);
|
|
31
|
+
if (serviceInstances) {
|
|
32
|
+
serviceInstances.delete(instanceId);
|
|
33
|
+
if (serviceInstances.size === 0) {
|
|
34
|
+
this.serviceIndex.delete(instance.name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async heartbeat(instanceId) {
|
|
40
|
+
const instance = this.instances.get(instanceId);
|
|
41
|
+
if (instance) {
|
|
42
|
+
instance.lastHeartbeat = new Date();
|
|
43
|
+
instance.status = types_1.ServiceStatus.UP;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async getInstances(serviceName, filter) {
|
|
47
|
+
const instanceIds = this.serviceIndex.get(serviceName);
|
|
48
|
+
if (!instanceIds)
|
|
49
|
+
return [];
|
|
50
|
+
const instances = [];
|
|
51
|
+
for (const id of instanceIds) {
|
|
52
|
+
const instance = this.instances.get(id);
|
|
53
|
+
if (instance) {
|
|
54
|
+
instances.push(instance);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Apply filters
|
|
58
|
+
return (0, filter_1.applyServiceFilter)(instances, filter);
|
|
59
|
+
}
|
|
60
|
+
async getInstance(instanceId) {
|
|
61
|
+
return this.instances.get(instanceId) || null;
|
|
62
|
+
}
|
|
63
|
+
async getAllServices() {
|
|
64
|
+
return Array.from(this.serviceIndex.keys());
|
|
65
|
+
}
|
|
66
|
+
async updateStatus(instanceId, status) {
|
|
67
|
+
const instance = this.instances.get(instanceId);
|
|
68
|
+
if (instance) {
|
|
69
|
+
instance.status = status;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async cleanup() {
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
const expiredIds = [];
|
|
75
|
+
for (const [id, instance] of this.instances) {
|
|
76
|
+
const timeSinceHeartbeat = now - instance.lastHeartbeat.getTime();
|
|
77
|
+
if (timeSinceHeartbeat > this.expirationTime) {
|
|
78
|
+
expiredIds.push(id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const id of expiredIds) {
|
|
82
|
+
await this.deregister(id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.MemoryRegistryBackend = MemoryRegistryBackend;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis Registry Backend
|
|
3
|
+
* For production distributed service registry
|
|
4
|
+
*/
|
|
5
|
+
import { RegistryBackend } from './registry-backend';
|
|
6
|
+
import { ServiceInstance, ServiceFilter } from '../types';
|
|
7
|
+
import type { Redis } from 'ioredis';
|
|
8
|
+
export interface RedisBackendConfig {
|
|
9
|
+
host?: string;
|
|
10
|
+
port?: number;
|
|
11
|
+
password?: string;
|
|
12
|
+
db?: number;
|
|
13
|
+
keyPrefix?: string;
|
|
14
|
+
ttl?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class RedisRegistryBackend implements RegistryBackend {
|
|
17
|
+
private redis;
|
|
18
|
+
private readonly keyPrefix;
|
|
19
|
+
private readonly ttl;
|
|
20
|
+
private connected;
|
|
21
|
+
constructor(redis: Redis, config?: RedisBackendConfig);
|
|
22
|
+
/**
|
|
23
|
+
* Set up Redis connection event handlers for resilience
|
|
24
|
+
*/
|
|
25
|
+
private setupConnectionHandlers;
|
|
26
|
+
/**
|
|
27
|
+
* Check Redis connectivity before operations
|
|
28
|
+
*/
|
|
29
|
+
private ensureConnected;
|
|
30
|
+
/**
|
|
31
|
+
* Register a service instance
|
|
32
|
+
*/
|
|
33
|
+
register(instance: ServiceInstance): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Deregister a service instance
|
|
36
|
+
*/
|
|
37
|
+
deregister(instanceId: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Update service instance heartbeat
|
|
40
|
+
*/
|
|
41
|
+
heartbeat(instanceId: string): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Get all instances of a service (uses MGET for efficiency)
|
|
44
|
+
*/
|
|
45
|
+
getInstances(serviceName: string, filter?: ServiceFilter): Promise<ServiceInstance[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Get a specific service instance
|
|
48
|
+
*/
|
|
49
|
+
getInstance(instanceId: string): Promise<ServiceInstance | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Get all registered services using SCAN (safe for production)
|
|
52
|
+
*/
|
|
53
|
+
getAllServices(): Promise<string[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Update service instance status
|
|
56
|
+
*/
|
|
57
|
+
updateStatus(instanceId: string, status: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Clean up expired instances
|
|
60
|
+
* Note: Redis handles expiration automatically via TTL
|
|
61
|
+
*/
|
|
62
|
+
cleanup(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Close Redis connection
|
|
65
|
+
*/
|
|
66
|
+
close(): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Get Redis key for instance
|
|
69
|
+
*/
|
|
70
|
+
private getInstanceKey;
|
|
71
|
+
/**
|
|
72
|
+
* Get Redis key for service set
|
|
73
|
+
*/
|
|
74
|
+
private getServiceSetKey;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=redis-backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-backend.d.ts","sourceRoot":"","sources":["../../src/backends/redis-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAC;AAIzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAQ;gBAEb,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,kBAAuB;IAUzD;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAuB/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxD;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBnD;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBlD;;OAEG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA8B3F;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAmBtE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAoBzC;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrE;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAGzB"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Redis Registry Backend
|
|
4
|
+
* For production distributed service registry
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RedisRegistryBackend = void 0;
|
|
8
|
+
const types_1 = require("../types");
|
|
9
|
+
const filter_1 = require("../utils/filter");
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
11
|
+
const validation_1 = require("../utils/validation");
|
|
12
|
+
class RedisRegistryBackend {
|
|
13
|
+
constructor(redis, config = {}) {
|
|
14
|
+
this.connected = true;
|
|
15
|
+
(0, validation_1.validateRedisBackendConfig)(config);
|
|
16
|
+
this.redis = redis;
|
|
17
|
+
this.keyPrefix = config.keyPrefix || 'hazeljs:discovery:';
|
|
18
|
+
this.ttl = config.ttl || 90; // 90 seconds default
|
|
19
|
+
this.setupConnectionHandlers();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Set up Redis connection event handlers for resilience
|
|
23
|
+
*/
|
|
24
|
+
setupConnectionHandlers() {
|
|
25
|
+
const logger = logger_1.DiscoveryLogger.getLogger();
|
|
26
|
+
this.redis.on('error', (err) => {
|
|
27
|
+
this.connected = false;
|
|
28
|
+
logger.error('Redis connection error', err);
|
|
29
|
+
});
|
|
30
|
+
this.redis.on('connect', () => {
|
|
31
|
+
this.connected = true;
|
|
32
|
+
logger.info('Redis connected');
|
|
33
|
+
});
|
|
34
|
+
this.redis.on('reconnecting', () => {
|
|
35
|
+
logger.warn('Redis reconnecting...');
|
|
36
|
+
});
|
|
37
|
+
this.redis.on('close', () => {
|
|
38
|
+
this.connected = false;
|
|
39
|
+
logger.warn('Redis connection closed');
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check Redis connectivity before operations
|
|
44
|
+
*/
|
|
45
|
+
ensureConnected() {
|
|
46
|
+
if (!this.connected) {
|
|
47
|
+
throw new Error('Redis backend is not connected');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Register a service instance
|
|
52
|
+
*/
|
|
53
|
+
async register(instance) {
|
|
54
|
+
this.ensureConnected();
|
|
55
|
+
const key = this.getInstanceKey(instance.id);
|
|
56
|
+
const serviceSetKey = this.getServiceSetKey(instance.name);
|
|
57
|
+
// Store instance data
|
|
58
|
+
await this.redis.setex(key, this.ttl, JSON.stringify(instance));
|
|
59
|
+
// Add to service set
|
|
60
|
+
await this.redis.sadd(serviceSetKey, instance.id);
|
|
61
|
+
// Set expiration on service set (will be refreshed on heartbeat)
|
|
62
|
+
await this.redis.expire(serviceSetKey, this.ttl * 2);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Deregister a service instance
|
|
66
|
+
*/
|
|
67
|
+
async deregister(instanceId) {
|
|
68
|
+
this.ensureConnected();
|
|
69
|
+
const key = this.getInstanceKey(instanceId);
|
|
70
|
+
// Get instance to find service name
|
|
71
|
+
const data = await this.redis.get(key);
|
|
72
|
+
if (data) {
|
|
73
|
+
const instance = JSON.parse(data);
|
|
74
|
+
const serviceSetKey = this.getServiceSetKey(instance.name);
|
|
75
|
+
// Remove from service set
|
|
76
|
+
await this.redis.srem(serviceSetKey, instanceId);
|
|
77
|
+
// Delete instance data
|
|
78
|
+
await this.redis.del(key);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Update service instance heartbeat
|
|
83
|
+
*/
|
|
84
|
+
async heartbeat(instanceId) {
|
|
85
|
+
this.ensureConnected();
|
|
86
|
+
const key = this.getInstanceKey(instanceId);
|
|
87
|
+
// Get current instance
|
|
88
|
+
const data = await this.redis.get(key);
|
|
89
|
+
if (data) {
|
|
90
|
+
const instance = JSON.parse(data);
|
|
91
|
+
// Update heartbeat and status
|
|
92
|
+
instance.lastHeartbeat = new Date();
|
|
93
|
+
instance.status = types_1.ServiceStatus.UP;
|
|
94
|
+
// Update with new TTL
|
|
95
|
+
await this.redis.setex(key, this.ttl, JSON.stringify(instance));
|
|
96
|
+
// Refresh service set TTL
|
|
97
|
+
const serviceSetKey = this.getServiceSetKey(instance.name);
|
|
98
|
+
await this.redis.expire(serviceSetKey, this.ttl * 2);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get all instances of a service (uses MGET for efficiency)
|
|
103
|
+
*/
|
|
104
|
+
async getInstances(serviceName, filter) {
|
|
105
|
+
this.ensureConnected();
|
|
106
|
+
const serviceSetKey = this.getServiceSetKey(serviceName);
|
|
107
|
+
// Get all instance IDs for this service
|
|
108
|
+
const instanceIds = await this.redis.smembers(serviceSetKey);
|
|
109
|
+
if (instanceIds.length === 0) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
// Batch-fetch all instances with MGET
|
|
113
|
+
const keys = instanceIds.map((id) => this.getInstanceKey(id));
|
|
114
|
+
const results = await this.redis.mget(...keys);
|
|
115
|
+
const instances = [];
|
|
116
|
+
for (const data of results) {
|
|
117
|
+
if (data) {
|
|
118
|
+
const instance = JSON.parse(data);
|
|
119
|
+
instance.lastHeartbeat = new Date(instance.lastHeartbeat);
|
|
120
|
+
instance.registeredAt = new Date(instance.registeredAt);
|
|
121
|
+
instances.push(instance);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Apply filters
|
|
125
|
+
return (0, filter_1.applyServiceFilter)(instances, filter);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get a specific service instance
|
|
129
|
+
*/
|
|
130
|
+
async getInstance(instanceId) {
|
|
131
|
+
this.ensureConnected();
|
|
132
|
+
const key = this.getInstanceKey(instanceId);
|
|
133
|
+
const data = await this.redis.get(key);
|
|
134
|
+
if (!data) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const instance = JSON.parse(data);
|
|
138
|
+
// Convert date strings back to Date objects
|
|
139
|
+
instance.lastHeartbeat = new Date(instance.lastHeartbeat);
|
|
140
|
+
instance.registeredAt = new Date(instance.registeredAt);
|
|
141
|
+
return instance;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get all registered services using SCAN (safe for production)
|
|
145
|
+
*/
|
|
146
|
+
async getAllServices() {
|
|
147
|
+
this.ensureConnected();
|
|
148
|
+
const pattern = `${this.keyPrefix}service:*`;
|
|
149
|
+
const prefixLen = `${this.keyPrefix}service:`.length;
|
|
150
|
+
const services = [];
|
|
151
|
+
let cursor = '0';
|
|
152
|
+
do {
|
|
153
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
|
|
154
|
+
cursor = nextCursor;
|
|
155
|
+
for (const key of keys) {
|
|
156
|
+
services.push(key.substring(prefixLen));
|
|
157
|
+
}
|
|
158
|
+
} while (cursor !== '0');
|
|
159
|
+
return services;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Update service instance status
|
|
163
|
+
*/
|
|
164
|
+
async updateStatus(instanceId, status) {
|
|
165
|
+
this.ensureConnected();
|
|
166
|
+
const key = this.getInstanceKey(instanceId);
|
|
167
|
+
const data = await this.redis.get(key);
|
|
168
|
+
if (data) {
|
|
169
|
+
const instance = JSON.parse(data);
|
|
170
|
+
instance.status = status;
|
|
171
|
+
await this.redis.setex(key, this.ttl, JSON.stringify(instance));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Clean up expired instances
|
|
176
|
+
* Note: Redis handles expiration automatically via TTL
|
|
177
|
+
*/
|
|
178
|
+
async cleanup() {
|
|
179
|
+
this.ensureConnected();
|
|
180
|
+
// Redis automatically removes expired keys
|
|
181
|
+
// This method cleans up stale entries in service sets
|
|
182
|
+
const services = await this.getAllServices();
|
|
183
|
+
for (const serviceName of services) {
|
|
184
|
+
const serviceSetKey = this.getServiceSetKey(serviceName);
|
|
185
|
+
const instanceIds = await this.redis.smembers(serviceSetKey);
|
|
186
|
+
// Check each instance and remove if expired
|
|
187
|
+
for (const id of instanceIds) {
|
|
188
|
+
const exists = await this.redis.exists(this.getInstanceKey(id));
|
|
189
|
+
if (!exists) {
|
|
190
|
+
// Instance key expired but still in set, remove it
|
|
191
|
+
await this.redis.srem(serviceSetKey, id);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Remove empty service sets
|
|
195
|
+
const count = await this.redis.scard(serviceSetKey);
|
|
196
|
+
if (count === 0) {
|
|
197
|
+
await this.redis.del(serviceSetKey);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Close Redis connection
|
|
203
|
+
*/
|
|
204
|
+
async close() {
|
|
205
|
+
await this.redis.quit();
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get Redis key for instance
|
|
209
|
+
*/
|
|
210
|
+
getInstanceKey(instanceId) {
|
|
211
|
+
return `${this.keyPrefix}instance:${instanceId}`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get Redis key for service set
|
|
215
|
+
*/
|
|
216
|
+
getServiceSetKey(serviceName) {
|
|
217
|
+
return `${this.keyPrefix}service:${serviceName}`;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.RedisRegistryBackend = RedisRegistryBackend;
|