@roomkit/gateway 1.0.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 +93 -0
- package/dist/src/admin/admin.controller.d.ts +187 -0
- package/dist/src/admin/admin.controller.d.ts.map +1 -0
- package/dist/src/admin/admin.controller.js +187 -0
- package/dist/src/admin/admin.controller.js.map +1 -0
- package/dist/src/admin/admin.module.d.ts +3 -0
- package/dist/src/admin/admin.module.d.ts.map +1 -0
- package/dist/src/admin/admin.module.js +28 -0
- package/dist/src/admin/admin.module.js.map +1 -0
- package/dist/src/admin/admin.service.d.ts +196 -0
- package/dist/src/admin/admin.service.d.ts.map +1 -0
- package/dist/src/admin/admin.service.js +322 -0
- package/dist/src/admin/admin.service.js.map +1 -0
- package/dist/src/config/config.module.d.ts +3 -0
- package/dist/src/config/config.module.d.ts.map +1 -0
- package/dist/src/config/config.module.js +22 -0
- package/dist/src/config/config.module.js.map +1 -0
- package/dist/src/config/config.service.d.ts +19 -0
- package/dist/src/config/config.service.d.ts.map +1 -0
- package/dist/src/config/config.service.js +55 -0
- package/dist/src/config/config.service.js.map +1 -0
- package/dist/src/config/gateway.config.d.ts +64 -0
- package/dist/src/config/gateway.config.d.ts.map +1 -0
- package/dist/src/config/gateway.config.js +43 -0
- package/dist/src/config/gateway.config.js.map +1 -0
- package/dist/src/connection/connection.module.d.ts +3 -0
- package/dist/src/connection/connection.module.d.ts.map +1 -0
- package/dist/src/connection/connection.module.js +22 -0
- package/dist/src/connection/connection.module.js.map +1 -0
- package/dist/src/connection/connection.service.d.ts +93 -0
- package/dist/src/connection/connection.service.d.ts.map +1 -0
- package/dist/src/connection/connection.service.js +257 -0
- package/dist/src/connection/connection.service.js.map +1 -0
- package/dist/src/gateway.module.d.ts +8 -0
- package/dist/src/gateway.module.d.ts.map +1 -0
- package/dist/src/gateway.module.js +44 -0
- package/dist/src/gateway.module.js.map +1 -0
- package/dist/src/index.d.ts +38 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +97 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lifecycle/graceful-shutdown.service.d.ts +65 -0
- package/dist/src/lifecycle/graceful-shutdown.service.d.ts.map +1 -0
- package/dist/src/lifecycle/graceful-shutdown.service.js +187 -0
- package/dist/src/lifecycle/graceful-shutdown.service.js.map +1 -0
- package/dist/src/lifecycle/index.d.ts +2 -0
- package/dist/src/lifecycle/index.d.ts.map +1 -0
- package/dist/src/lifecycle/index.js +18 -0
- package/dist/src/lifecycle/index.js.map +1 -0
- package/dist/src/main.d.ts +2 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +56 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/metrics/metrics.controller.d.ts +13 -0
- package/dist/src/metrics/metrics.controller.d.ts.map +1 -0
- package/dist/src/metrics/metrics.controller.js +42 -0
- package/dist/src/metrics/metrics.controller.js.map +1 -0
- package/dist/src/metrics/metrics.module.d.ts +3 -0
- package/dist/src/metrics/metrics.module.d.ts.map +1 -0
- package/dist/src/metrics/metrics.module.js +26 -0
- package/dist/src/metrics/metrics.module.js.map +1 -0
- package/dist/src/metrics/metrics.service.d.ts +81 -0
- package/dist/src/metrics/metrics.service.d.ts.map +1 -0
- package/dist/src/metrics/metrics.service.js +255 -0
- package/dist/src/metrics/metrics.service.js.map +1 -0
- package/dist/src/ratelimit/rate-limit.module.d.ts +3 -0
- package/dist/src/ratelimit/rate-limit.module.d.ts.map +1 -0
- package/dist/src/ratelimit/rate-limit.module.js +23 -0
- package/dist/src/ratelimit/rate-limit.module.js.map +1 -0
- package/dist/src/ratelimit/rate-limiter.service.d.ts +68 -0
- package/dist/src/ratelimit/rate-limiter.service.d.ts.map +1 -0
- package/dist/src/ratelimit/rate-limiter.service.js +201 -0
- package/dist/src/ratelimit/rate-limiter.service.js.map +1 -0
- package/dist/src/redis/redis.module.d.ts +3 -0
- package/dist/src/redis/redis.module.d.ts.map +1 -0
- package/dist/src/redis/redis.module.js +22 -0
- package/dist/src/redis/redis.module.js.map +1 -0
- package/dist/src/redis/redis.service.d.ts +68 -0
- package/dist/src/redis/redis.service.d.ts.map +1 -0
- package/dist/src/redis/redis.service.js +281 -0
- package/dist/src/redis/redis.service.js.map +1 -0
- package/dist/src/session/session.module.d.ts +3 -0
- package/dist/src/session/session.module.d.ts.map +1 -0
- package/dist/src/session/session.module.js +21 -0
- package/dist/src/session/session.module.js.map +1 -0
- package/dist/src/session/session.service.d.ts +64 -0
- package/dist/src/session/session.service.d.ts.map +1 -0
- package/dist/src/session/session.service.js +206 -0
- package/dist/src/session/session.service.js.map +1 -0
- package/dist/src/ws/message-router.service.d.ts +133 -0
- package/dist/src/ws/message-router.service.d.ts.map +1 -0
- package/dist/src/ws/message-router.service.js +715 -0
- package/dist/src/ws/message-router.service.js.map +1 -0
- package/dist/src/ws/response-handler.service.d.ts +53 -0
- package/dist/src/ws/response-handler.service.d.ts.map +1 -0
- package/dist/src/ws/response-handler.service.js +282 -0
- package/dist/src/ws/response-handler.service.js.map +1 -0
- package/dist/src/ws/ws.gateway.d.ts +46 -0
- package/dist/src/ws/ws.gateway.d.ts.map +1 -0
- package/dist/src/ws/ws.gateway.js +244 -0
- package/dist/src/ws/ws.gateway.js.map +1 -0
- package/dist/src/ws/ws.module.d.ts +8 -0
- package/dist/src/ws/ws.module.d.ts.map +1 -0
- package/dist/src/ws/ws.module.js +39 -0
- package/dist/src/ws/ws.module.js.map +1 -0
- package/dist/test/connection/connection.service.spec.d.ts +2 -0
- package/dist/test/connection/connection.service.spec.d.ts.map +1 -0
- package/dist/test/connection/connection.service.spec.js +204 -0
- package/dist/test/connection/connection.service.spec.js.map +1 -0
- package/dist/test/e2e/gateway-worker.e2e.spec.d.ts +2 -0
- package/dist/test/e2e/gateway-worker.e2e.spec.d.ts.map +1 -0
- package/dist/test/e2e/gateway-worker.e2e.spec.js +412 -0
- package/dist/test/e2e/gateway-worker.e2e.spec.js.map +1 -0
- package/dist/test/integration/admin-api.spec.d.ts +2 -0
- package/dist/test/integration/admin-api.spec.d.ts.map +1 -0
- package/dist/test/integration/admin-api.spec.js +218 -0
- package/dist/test/integration/admin-api.spec.js.map +1 -0
- package/dist/test/ratelimit/rate-limiter.service.spec.d.ts +2 -0
- package/dist/test/ratelimit/rate-limiter.service.spec.d.ts.map +1 -0
- package/dist/test/ratelimit/rate-limiter.service.spec.js +139 -0
- package/dist/test/ratelimit/rate-limiter.service.spec.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +56 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/test/ws/message-router.service.spec.d.ts +2 -0
- package/dist/test/ws/message-router.service.spec.d.ts.map +1 -0
- package/dist/test/ws/message-router.service.spec.js +403 -0
- package/dist/test/ws/message-router.service.spec.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { Counter, Gauge, Histogram } from 'prom-client';
|
|
3
|
+
import { ConfigService } from '../config/config.service';
|
|
4
|
+
/**
|
|
5
|
+
* Gateway Prometheus 指标服务
|
|
6
|
+
* 提供连接、消息、限流等监控指标
|
|
7
|
+
*/
|
|
8
|
+
export declare class MetricsService implements OnModuleInit {
|
|
9
|
+
private readonly configService;
|
|
10
|
+
private readonly registry;
|
|
11
|
+
readonly connectionsTotal: Counter;
|
|
12
|
+
readonly connectionsCurrent: Gauge;
|
|
13
|
+
readonly connectionsAuthenticated: Gauge;
|
|
14
|
+
readonly connectionDuration: Histogram;
|
|
15
|
+
readonly messagesReceived: Counter;
|
|
16
|
+
readonly messagesSent: Counter;
|
|
17
|
+
readonly messageProcessingTime: Histogram;
|
|
18
|
+
readonly messagesByType: Counter;
|
|
19
|
+
readonly rateLimitRejections: Counter;
|
|
20
|
+
readonly connectionRateLimitHits: Counter;
|
|
21
|
+
readonly messageRateLimitHits: Counter;
|
|
22
|
+
readonly uptime: Gauge;
|
|
23
|
+
readonly memoryUsage: Gauge;
|
|
24
|
+
readonly wsErrors: Counter;
|
|
25
|
+
readonly wsReconnections: Counter;
|
|
26
|
+
constructor(configService: ConfigService);
|
|
27
|
+
onModuleInit(): void;
|
|
28
|
+
/**
|
|
29
|
+
* 启动定期指标收集
|
|
30
|
+
*/
|
|
31
|
+
private startMetricsCollection;
|
|
32
|
+
/**
|
|
33
|
+
* 获取所有指标(Prometheus 格式)
|
|
34
|
+
*/
|
|
35
|
+
getMetrics(): Promise<string>;
|
|
36
|
+
/**
|
|
37
|
+
* 获取指标 Content-Type
|
|
38
|
+
*/
|
|
39
|
+
getContentType(): string;
|
|
40
|
+
/**
|
|
41
|
+
* 记录新连接
|
|
42
|
+
*/
|
|
43
|
+
recordNewConnection(): void;
|
|
44
|
+
/**
|
|
45
|
+
* 记录连接断开
|
|
46
|
+
*/
|
|
47
|
+
recordDisconnection(durationSeconds: number): void;
|
|
48
|
+
/**
|
|
49
|
+
* 记录连接被拒绝
|
|
50
|
+
*/
|
|
51
|
+
recordConnectionRejected(reason: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* 更新当前连接数
|
|
54
|
+
*/
|
|
55
|
+
updateCurrentConnections(total: number, authenticated: number): void;
|
|
56
|
+
/**
|
|
57
|
+
* 记录收到的消息
|
|
58
|
+
*/
|
|
59
|
+
recordMessageReceived(msgId: number): void;
|
|
60
|
+
/**
|
|
61
|
+
* 记录发送的消息
|
|
62
|
+
*/
|
|
63
|
+
recordMessageSent(msgId: number): void;
|
|
64
|
+
/**
|
|
65
|
+
* 记录消息处理时间
|
|
66
|
+
*/
|
|
67
|
+
recordMessageProcessingTime(msgId: number, durationSeconds: number): void;
|
|
68
|
+
/**
|
|
69
|
+
* 记录消息限流
|
|
70
|
+
*/
|
|
71
|
+
recordMessageRateLimited(connectionId: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* 记录全局限流
|
|
74
|
+
*/
|
|
75
|
+
recordGlobalRateLimited(): void;
|
|
76
|
+
/**
|
|
77
|
+
* 记录 WebSocket 错误
|
|
78
|
+
*/
|
|
79
|
+
recordWsError(errorType: string): void;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=metrics.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.service.d.ts","sourceRoot":"","sources":["../../../src/metrics/metrics.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAEL,OAAO,EACP,KAAK,EACL,SAAS,EAEV,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;;GAGG;AACH,qBACa,cAAe,YAAW,YAAY;IA4BrC,OAAO,CAAC,QAAQ,CAAC,aAAa;IA3B1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IAGpC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,kBAAkB,EAAE,KAAK,CAAC;IACnC,QAAQ,CAAC,wBAAwB,EAAE,KAAK,CAAC;IACzC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,CAAC;IAGvC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,qBAAqB,EAAE,SAAS,CAAC;IAC1C,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IAGjC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAGvC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAG5B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;gBAEL,aAAa,EAAE,aAAa;IAoHzD,YAAY;IAWZ;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACH,mBAAmB;IAInB;;OAEG;IACH,mBAAmB,CAAC,eAAe,EAAE,MAAM;IAK3C;;OAEG;IACH,wBAAwB,CAAC,MAAM,EAAE,MAAM;IAQvC;;OAEG;IACH,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAK7D;;OAEG;IACH,qBAAqB,CAAC,KAAK,EAAE,MAAM;IAKnC;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM;IAK/B;;OAEG;IACH,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM;IAIlE;;OAEG;IACH,wBAAwB,CAAC,YAAY,EAAE,MAAM;IAK7C;;OAEG;IACH,uBAAuB;IAIvB;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM;CAGhC"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MetricsService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const prom_client_1 = require("prom-client");
|
|
15
|
+
const config_service_1 = require("../config/config.service");
|
|
16
|
+
/**
|
|
17
|
+
* Gateway Prometheus 指标服务
|
|
18
|
+
* 提供连接、消息、限流等监控指标
|
|
19
|
+
*/
|
|
20
|
+
let MetricsService = class MetricsService {
|
|
21
|
+
configService;
|
|
22
|
+
registry;
|
|
23
|
+
// 连接指标
|
|
24
|
+
connectionsTotal;
|
|
25
|
+
connectionsCurrent;
|
|
26
|
+
connectionsAuthenticated;
|
|
27
|
+
connectionDuration;
|
|
28
|
+
// 消息指标
|
|
29
|
+
messagesReceived;
|
|
30
|
+
messagesSent;
|
|
31
|
+
messageProcessingTime;
|
|
32
|
+
messagesByType;
|
|
33
|
+
// 限流指标
|
|
34
|
+
rateLimitRejections;
|
|
35
|
+
connectionRateLimitHits;
|
|
36
|
+
messageRateLimitHits;
|
|
37
|
+
// 系统指标
|
|
38
|
+
uptime;
|
|
39
|
+
memoryUsage;
|
|
40
|
+
// WebSocket 指标
|
|
41
|
+
wsErrors;
|
|
42
|
+
wsReconnections;
|
|
43
|
+
constructor(configService) {
|
|
44
|
+
this.configService = configService;
|
|
45
|
+
this.registry = new prom_client_1.Registry();
|
|
46
|
+
// 设置默认标签
|
|
47
|
+
this.registry.setDefaultLabels({
|
|
48
|
+
app: 'gateway',
|
|
49
|
+
instance: this.configService.gatewayId,
|
|
50
|
+
});
|
|
51
|
+
// 连接指标
|
|
52
|
+
this.connectionsTotal = new prom_client_1.Counter({
|
|
53
|
+
name: 'gateway_connections_total',
|
|
54
|
+
help: 'Total number of WebSocket connections',
|
|
55
|
+
labelNames: ['status'], // connected, disconnected, rejected
|
|
56
|
+
registers: [this.registry],
|
|
57
|
+
});
|
|
58
|
+
this.connectionsCurrent = new prom_client_1.Gauge({
|
|
59
|
+
name: 'gateway_connections_current',
|
|
60
|
+
help: 'Current number of active WebSocket connections',
|
|
61
|
+
registers: [this.registry],
|
|
62
|
+
});
|
|
63
|
+
this.connectionsAuthenticated = new prom_client_1.Gauge({
|
|
64
|
+
name: 'gateway_connections_authenticated',
|
|
65
|
+
help: 'Number of authenticated connections',
|
|
66
|
+
registers: [this.registry],
|
|
67
|
+
});
|
|
68
|
+
this.connectionDuration = new prom_client_1.Histogram({
|
|
69
|
+
name: 'gateway_connection_duration_seconds',
|
|
70
|
+
help: 'Duration of WebSocket connections in seconds',
|
|
71
|
+
buckets: [1, 5, 15, 30, 60, 120, 300, 600, 1800, 3600],
|
|
72
|
+
registers: [this.registry],
|
|
73
|
+
});
|
|
74
|
+
// 消息指标
|
|
75
|
+
this.messagesReceived = new prom_client_1.Counter({
|
|
76
|
+
name: 'gateway_messages_received_total',
|
|
77
|
+
help: 'Total number of messages received from clients',
|
|
78
|
+
labelNames: ['msg_type'],
|
|
79
|
+
registers: [this.registry],
|
|
80
|
+
});
|
|
81
|
+
this.messagesSent = new prom_client_1.Counter({
|
|
82
|
+
name: 'gateway_messages_sent_total',
|
|
83
|
+
help: 'Total number of messages sent to clients',
|
|
84
|
+
labelNames: ['msg_type'],
|
|
85
|
+
registers: [this.registry],
|
|
86
|
+
});
|
|
87
|
+
this.messageProcessingTime = new prom_client_1.Histogram({
|
|
88
|
+
name: 'gateway_message_processing_seconds',
|
|
89
|
+
help: 'Time spent processing messages',
|
|
90
|
+
buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
|
|
91
|
+
labelNames: ['msg_type'],
|
|
92
|
+
registers: [this.registry],
|
|
93
|
+
});
|
|
94
|
+
this.messagesByType = new prom_client_1.Counter({
|
|
95
|
+
name: 'gateway_messages_by_type_total',
|
|
96
|
+
help: 'Messages count by message type',
|
|
97
|
+
labelNames: ['direction', 'msg_id'],
|
|
98
|
+
registers: [this.registry],
|
|
99
|
+
});
|
|
100
|
+
// 限流指标
|
|
101
|
+
this.rateLimitRejections = new prom_client_1.Counter({
|
|
102
|
+
name: 'gateway_rate_limit_rejections_total',
|
|
103
|
+
help: 'Total number of rate limit rejections',
|
|
104
|
+
labelNames: ['type'], // connection, message, global
|
|
105
|
+
registers: [this.registry],
|
|
106
|
+
});
|
|
107
|
+
this.connectionRateLimitHits = new prom_client_1.Counter({
|
|
108
|
+
name: 'gateway_connection_rate_limit_hits_total',
|
|
109
|
+
help: 'Number of connection rate limit hits',
|
|
110
|
+
registers: [this.registry],
|
|
111
|
+
});
|
|
112
|
+
this.messageRateLimitHits = new prom_client_1.Counter({
|
|
113
|
+
name: 'gateway_message_rate_limit_hits_total',
|
|
114
|
+
help: 'Number of message rate limit hits',
|
|
115
|
+
labelNames: ['connection_id'],
|
|
116
|
+
registers: [this.registry],
|
|
117
|
+
});
|
|
118
|
+
// 系统指标
|
|
119
|
+
this.uptime = new prom_client_1.Gauge({
|
|
120
|
+
name: 'gateway_uptime_seconds',
|
|
121
|
+
help: 'Gateway uptime in seconds',
|
|
122
|
+
registers: [this.registry],
|
|
123
|
+
});
|
|
124
|
+
this.memoryUsage = new prom_client_1.Gauge({
|
|
125
|
+
name: 'gateway_memory_usage_bytes',
|
|
126
|
+
help: 'Memory usage in bytes',
|
|
127
|
+
labelNames: ['type'], // heap_used, heap_total, rss, external
|
|
128
|
+
registers: [this.registry],
|
|
129
|
+
});
|
|
130
|
+
// WebSocket 指标
|
|
131
|
+
this.wsErrors = new prom_client_1.Counter({
|
|
132
|
+
name: 'gateway_websocket_errors_total',
|
|
133
|
+
help: 'Total number of WebSocket errors',
|
|
134
|
+
labelNames: ['error_type'],
|
|
135
|
+
registers: [this.registry],
|
|
136
|
+
});
|
|
137
|
+
this.wsReconnections = new prom_client_1.Counter({
|
|
138
|
+
name: 'gateway_websocket_reconnections_total',
|
|
139
|
+
help: 'Total number of WebSocket reconnection attempts',
|
|
140
|
+
registers: [this.registry],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
onModuleInit() {
|
|
144
|
+
// 收集默认的 Node.js 指标
|
|
145
|
+
(0, prom_client_1.collectDefaultMetrics)({
|
|
146
|
+
register: this.registry,
|
|
147
|
+
prefix: 'gateway_nodejs_',
|
|
148
|
+
});
|
|
149
|
+
// 定期更新系统指标
|
|
150
|
+
this.startMetricsCollection();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 启动定期指标收集
|
|
154
|
+
*/
|
|
155
|
+
startMetricsCollection() {
|
|
156
|
+
const startTime = Date.now();
|
|
157
|
+
setInterval(() => {
|
|
158
|
+
// 更新运行时间
|
|
159
|
+
this.uptime.set((Date.now() - startTime) / 1000);
|
|
160
|
+
// 更新内存使用
|
|
161
|
+
const memory = process.memoryUsage();
|
|
162
|
+
this.memoryUsage.labels('heap_used').set(memory.heapUsed);
|
|
163
|
+
this.memoryUsage.labels('heap_total').set(memory.heapTotal);
|
|
164
|
+
this.memoryUsage.labels('rss').set(memory.rss);
|
|
165
|
+
this.memoryUsage.labels('external').set(memory.external);
|
|
166
|
+
}, 5000);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 获取所有指标(Prometheus 格式)
|
|
170
|
+
*/
|
|
171
|
+
async getMetrics() {
|
|
172
|
+
return this.registry.metrics();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 获取指标 Content-Type
|
|
176
|
+
*/
|
|
177
|
+
getContentType() {
|
|
178
|
+
return this.registry.contentType;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 记录新连接
|
|
182
|
+
*/
|
|
183
|
+
recordNewConnection() {
|
|
184
|
+
this.connectionsTotal.labels('connected').inc();
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 记录连接断开
|
|
188
|
+
*/
|
|
189
|
+
recordDisconnection(durationSeconds) {
|
|
190
|
+
this.connectionsTotal.labels('disconnected').inc();
|
|
191
|
+
this.connectionDuration.observe(durationSeconds);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 记录连接被拒绝
|
|
195
|
+
*/
|
|
196
|
+
recordConnectionRejected(reason) {
|
|
197
|
+
this.connectionsTotal.labels('rejected').inc();
|
|
198
|
+
if (reason === 'rate_limit') {
|
|
199
|
+
this.rateLimitRejections.labels('connection').inc();
|
|
200
|
+
this.connectionRateLimitHits.inc();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* 更新当前连接数
|
|
205
|
+
*/
|
|
206
|
+
updateCurrentConnections(total, authenticated) {
|
|
207
|
+
this.connectionsCurrent.set(total);
|
|
208
|
+
this.connectionsAuthenticated.set(authenticated);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 记录收到的消息
|
|
212
|
+
*/
|
|
213
|
+
recordMessageReceived(msgId) {
|
|
214
|
+
this.messagesReceived.labels(String(msgId)).inc();
|
|
215
|
+
this.messagesByType.labels('inbound', String(msgId)).inc();
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 记录发送的消息
|
|
219
|
+
*/
|
|
220
|
+
recordMessageSent(msgId) {
|
|
221
|
+
this.messagesSent.labels(String(msgId)).inc();
|
|
222
|
+
this.messagesByType.labels('outbound', String(msgId)).inc();
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* 记录消息处理时间
|
|
226
|
+
*/
|
|
227
|
+
recordMessageProcessingTime(msgId, durationSeconds) {
|
|
228
|
+
this.messageProcessingTime.labels(String(msgId)).observe(durationSeconds);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 记录消息限流
|
|
232
|
+
*/
|
|
233
|
+
recordMessageRateLimited(connectionId) {
|
|
234
|
+
this.rateLimitRejections.labels('message').inc();
|
|
235
|
+
this.messageRateLimitHits.labels(connectionId).inc();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* 记录全局限流
|
|
239
|
+
*/
|
|
240
|
+
recordGlobalRateLimited() {
|
|
241
|
+
this.rateLimitRejections.labels('global').inc();
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 记录 WebSocket 错误
|
|
245
|
+
*/
|
|
246
|
+
recordWsError(errorType) {
|
|
247
|
+
this.wsErrors.labels(errorType).inc();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
exports.MetricsService = MetricsService;
|
|
251
|
+
exports.MetricsService = MetricsService = __decorate([
|
|
252
|
+
(0, common_1.Injectable)(),
|
|
253
|
+
__metadata("design:paramtypes", [config_service_1.ConfigService])
|
|
254
|
+
], MetricsService);
|
|
255
|
+
//# sourceMappingURL=metrics.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.service.js","sourceRoot":"","sources":["../../../src/metrics/metrics.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.module.d.ts","sourceRoot":"","sources":["../../../src/ratelimit/rate-limit.module.ts"],"names":[],"mappings":"AAIA,qBAKa,eAAe;CAAG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.RateLimitModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const rate_limiter_service_1 = require("./rate-limiter.service");
|
|
12
|
+
const config_module_1 = require("../config/config.module");
|
|
13
|
+
let RateLimitModule = class RateLimitModule {
|
|
14
|
+
};
|
|
15
|
+
exports.RateLimitModule = RateLimitModule;
|
|
16
|
+
exports.RateLimitModule = RateLimitModule = __decorate([
|
|
17
|
+
(0, common_1.Module)({
|
|
18
|
+
imports: [config_module_1.ConfigModule],
|
|
19
|
+
providers: [rate_limiter_service_1.RateLimiterService],
|
|
20
|
+
exports: [rate_limiter_service_1.RateLimiterService],
|
|
21
|
+
})
|
|
22
|
+
], RateLimitModule);
|
|
23
|
+
//# sourceMappingURL=rate-limit.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.module.js","sourceRoot":"","sources":["../../../src/ratelimit/rate-limit.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,iEAA4D;AAC5D,2DAAuD;AAOhD,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAL3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,4BAAY,CAAC;QACvB,SAAS,EAAE,CAAC,yCAAkB,CAAC;QAC/B,OAAO,EAAE,CAAC,yCAAkB,CAAC;KAC9B,CAAC;GACW,eAAe,CAAG"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ConfigService } from '../config/config.service';
|
|
2
|
+
/**
|
|
3
|
+
* 限流服务
|
|
4
|
+
* 提供连接限流和消息限流功能
|
|
5
|
+
*/
|
|
6
|
+
export declare class RateLimiterService {
|
|
7
|
+
private readonly configService;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
private readonly connectionRateLimit;
|
|
10
|
+
private readonly messageRateLimitPerConnection;
|
|
11
|
+
private readonly messageBurstLimit;
|
|
12
|
+
private readonly globalMessageRateLimit;
|
|
13
|
+
private readonly windowSize;
|
|
14
|
+
private readonly windowSlots;
|
|
15
|
+
private readonly slotDuration;
|
|
16
|
+
private connectionWindow;
|
|
17
|
+
private globalMessageWindow;
|
|
18
|
+
private connectionMessageWindows;
|
|
19
|
+
private connectionBurstCounts;
|
|
20
|
+
constructor(configService: ConfigService);
|
|
21
|
+
/**
|
|
22
|
+
* 创建新的滑动窗口
|
|
23
|
+
*/
|
|
24
|
+
private createWindow;
|
|
25
|
+
/**
|
|
26
|
+
* 获取当前窗口分片索引
|
|
27
|
+
*/
|
|
28
|
+
private getCurrentSlot;
|
|
29
|
+
/**
|
|
30
|
+
* 滑动窗口并返回总计数
|
|
31
|
+
*/
|
|
32
|
+
private slideAndCount;
|
|
33
|
+
/**
|
|
34
|
+
* 增加窗口计数
|
|
35
|
+
*/
|
|
36
|
+
private incrementWindow;
|
|
37
|
+
/**
|
|
38
|
+
* 检查连接速率限制
|
|
39
|
+
* @returns true 如果允许连接,false 如果被限流
|
|
40
|
+
*/
|
|
41
|
+
checkConnectionRate(): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 检查消息速率限制
|
|
44
|
+
* @param connectionId 连接ID
|
|
45
|
+
* @returns { allowed: boolean, retryAfter?: number }
|
|
46
|
+
*/
|
|
47
|
+
checkMessageRate(connectionId: string): {
|
|
48
|
+
allowed: boolean;
|
|
49
|
+
retryAfter?: number;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* 移除连接的限流计数器
|
|
53
|
+
*/
|
|
54
|
+
removeConnection(connectionId: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* 清理过期的窗口数据
|
|
57
|
+
*/
|
|
58
|
+
private cleanupExpiredWindows;
|
|
59
|
+
/**
|
|
60
|
+
* 获取当前统计信息
|
|
61
|
+
*/
|
|
62
|
+
getStats(): {
|
|
63
|
+
connectionRate: number;
|
|
64
|
+
globalMessageRate: number;
|
|
65
|
+
trackedConnections: number;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=rate-limiter.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.service.d.ts","sourceRoot":"","sources":["../../../src/ratelimit/rate-limiter.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAUzD;;;GAGG;AACH,qBACa,kBAAkB;IA4BjB,OAAO,CAAC,QAAQ,CAAC,aAAa;IA3B1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAG9D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAE7C,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAAS;IAEvD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAGhD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IAEnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAM;IAElC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IAGtC,OAAO,CAAC,gBAAgB,CAAgB;IAExC,OAAO,CAAC,mBAAmB,CAAgB;IAE3C,OAAO,CAAC,wBAAwB,CAAoC;IAEpE,OAAO,CAAC,qBAAqB,CAA2D;gBAE3D,aAAa,EAAE,aAAa;IAuBzD;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAKtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAsBrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;;OAGG;IACH,mBAAmB,IAAI,OAAO;IAY9B;;;;OAIG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IA6CjF;;OAEG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAK5C;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAiB7B;;OAEG;IACH,QAAQ,IAAI;QACV,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,kBAAkB,EAAE,MAAM,CAAC;KAC5B;CAOF"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var RateLimiterService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.RateLimiterService = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const config_service_1 = require("../config/config.service");
|
|
16
|
+
/**
|
|
17
|
+
* 限流服务
|
|
18
|
+
* 提供连接限流和消息限流功能
|
|
19
|
+
*/
|
|
20
|
+
let RateLimiterService = RateLimiterService_1 = class RateLimiterService {
|
|
21
|
+
configService;
|
|
22
|
+
logger = new common_1.Logger(RateLimiterService_1.name);
|
|
23
|
+
// 连接速率限制(每秒新连接数)
|
|
24
|
+
connectionRateLimit;
|
|
25
|
+
// 消息速率限制(每连接每秒)
|
|
26
|
+
messageRateLimitPerConnection;
|
|
27
|
+
// 消息突发上限
|
|
28
|
+
messageBurstLimit;
|
|
29
|
+
// 全局消息速率限制(每秒)
|
|
30
|
+
globalMessageRateLimit;
|
|
31
|
+
// 滑动窗口大小(毫秒)
|
|
32
|
+
windowSize = 1000;
|
|
33
|
+
// 窗口分片数量
|
|
34
|
+
windowSlots = 10;
|
|
35
|
+
// 每个分片时长
|
|
36
|
+
slotDuration;
|
|
37
|
+
// 连接速率计数
|
|
38
|
+
connectionWindow;
|
|
39
|
+
// 全局消息速率计数
|
|
40
|
+
globalMessageWindow;
|
|
41
|
+
// 每连接消息速率计数
|
|
42
|
+
connectionMessageWindows = new Map();
|
|
43
|
+
// 每连接突发计数(短时间内)
|
|
44
|
+
connectionBurstCounts = new Map();
|
|
45
|
+
constructor(configService) {
|
|
46
|
+
this.configService = configService;
|
|
47
|
+
// 从配置读取限流参数,使用合理的默认值
|
|
48
|
+
this.connectionRateLimit = configService.connectionRateLimit ?? 100;
|
|
49
|
+
this.messageRateLimitPerConnection = configService.messageRateLimitPerConnection ?? 50;
|
|
50
|
+
this.messageBurstLimit = configService.messageBurstLimit ?? 100;
|
|
51
|
+
this.globalMessageRateLimit = configService.globalMessageRateLimit ?? 50000;
|
|
52
|
+
this.slotDuration = this.windowSize / this.windowSlots;
|
|
53
|
+
// 初始化滑动窗口
|
|
54
|
+
this.connectionWindow = this.createWindow();
|
|
55
|
+
this.globalMessageWindow = this.createWindow();
|
|
56
|
+
// 定期清理过期的连接计数器
|
|
57
|
+
setInterval(() => this.cleanupExpiredWindows(), 60000);
|
|
58
|
+
this.logger.log(`RateLimiter initialized: connRate=${this.connectionRateLimit}/s, ` +
|
|
59
|
+
`msgRate=${this.messageRateLimitPerConnection}/s, ` +
|
|
60
|
+
`burst=${this.messageBurstLimit}, globalRate=${this.globalMessageRateLimit}/s`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 创建新的滑动窗口
|
|
64
|
+
*/
|
|
65
|
+
createWindow() {
|
|
66
|
+
return {
|
|
67
|
+
counts: new Array(this.windowSlots).fill(0),
|
|
68
|
+
windowStartTime: Date.now(),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 获取当前窗口分片索引
|
|
73
|
+
*/
|
|
74
|
+
getCurrentSlot(window) {
|
|
75
|
+
const elapsed = Date.now() - window.windowStartTime;
|
|
76
|
+
return Math.floor(elapsed / this.slotDuration) % this.windowSlots;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 滑动窗口并返回总计数
|
|
80
|
+
*/
|
|
81
|
+
slideAndCount(window) {
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const elapsed = now - window.windowStartTime;
|
|
84
|
+
// 如果超过一个完整窗口,重置
|
|
85
|
+
if (elapsed >= this.windowSize) {
|
|
86
|
+
const slotsToReset = Math.min(Math.floor(elapsed / this.slotDuration), this.windowSlots);
|
|
87
|
+
for (let i = 0; i < slotsToReset; i++) {
|
|
88
|
+
const slotIndex = (this.getCurrentSlot(window) - i + this.windowSlots) % this.windowSlots;
|
|
89
|
+
window.counts[slotIndex] = 0;
|
|
90
|
+
}
|
|
91
|
+
window.windowStartTime = now - (elapsed % this.windowSize);
|
|
92
|
+
}
|
|
93
|
+
return window.counts.reduce((sum, count) => sum + count, 0);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 增加窗口计数
|
|
97
|
+
*/
|
|
98
|
+
incrementWindow(window) {
|
|
99
|
+
this.slideAndCount(window); // 先滑动窗口
|
|
100
|
+
const slot = this.getCurrentSlot(window);
|
|
101
|
+
window.counts[slot]++;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 检查连接速率限制
|
|
105
|
+
* @returns true 如果允许连接,false 如果被限流
|
|
106
|
+
*/
|
|
107
|
+
checkConnectionRate() {
|
|
108
|
+
const currentRate = this.slideAndCount(this.connectionWindow);
|
|
109
|
+
if (currentRate >= this.connectionRateLimit) {
|
|
110
|
+
this.logger.warn(`Connection rate limit exceeded: ${currentRate}/${this.connectionRateLimit}`);
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
this.incrementWindow(this.connectionWindow);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 检查消息速率限制
|
|
118
|
+
* @param connectionId 连接ID
|
|
119
|
+
* @returns { allowed: boolean, retryAfter?: number }
|
|
120
|
+
*/
|
|
121
|
+
checkMessageRate(connectionId) {
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
// 1. 检查突发限制
|
|
124
|
+
const burst = this.connectionBurstCounts.get(connectionId);
|
|
125
|
+
if (burst) {
|
|
126
|
+
if (now < burst.resetTime) {
|
|
127
|
+
if (burst.count >= this.messageBurstLimit) {
|
|
128
|
+
const retryAfter = burst.resetTime - now;
|
|
129
|
+
return { allowed: false, retryAfter };
|
|
130
|
+
}
|
|
131
|
+
burst.count++;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
this.connectionBurstCounts.set(connectionId, { count: 1, resetTime: now + 1000 });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
this.connectionBurstCounts.set(connectionId, { count: 1, resetTime: now + 1000 });
|
|
139
|
+
}
|
|
140
|
+
// 2. 检查连接消息速率
|
|
141
|
+
let connWindow = this.connectionMessageWindows.get(connectionId);
|
|
142
|
+
if (!connWindow) {
|
|
143
|
+
connWindow = this.createWindow();
|
|
144
|
+
this.connectionMessageWindows.set(connectionId, connWindow);
|
|
145
|
+
}
|
|
146
|
+
const connRate = this.slideAndCount(connWindow);
|
|
147
|
+
if (connRate >= this.messageRateLimitPerConnection) {
|
|
148
|
+
return { allowed: false, retryAfter: 1000 };
|
|
149
|
+
}
|
|
150
|
+
// 3. 检查全局消息速率
|
|
151
|
+
const globalRate = this.slideAndCount(this.globalMessageWindow);
|
|
152
|
+
if (globalRate >= this.globalMessageRateLimit) {
|
|
153
|
+
this.logger.warn(`Global message rate limit exceeded: ${globalRate}/${this.globalMessageRateLimit}`);
|
|
154
|
+
return { allowed: false, retryAfter: 500 };
|
|
155
|
+
}
|
|
156
|
+
// 更新计数
|
|
157
|
+
this.incrementWindow(connWindow);
|
|
158
|
+
this.incrementWindow(this.globalMessageWindow);
|
|
159
|
+
return { allowed: true };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 移除连接的限流计数器
|
|
163
|
+
*/
|
|
164
|
+
removeConnection(connectionId) {
|
|
165
|
+
this.connectionMessageWindows.delete(connectionId);
|
|
166
|
+
this.connectionBurstCounts.delete(connectionId);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 清理过期的窗口数据
|
|
170
|
+
*/
|
|
171
|
+
cleanupExpiredWindows() {
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
const expireThreshold = now - this.windowSize * 2;
|
|
174
|
+
for (const [connId, window] of this.connectionMessageWindows) {
|
|
175
|
+
if (window.windowStartTime < expireThreshold) {
|
|
176
|
+
this.connectionMessageWindows.delete(connId);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const [connId, burst] of this.connectionBurstCounts) {
|
|
180
|
+
if (burst.resetTime < now) {
|
|
181
|
+
this.connectionBurstCounts.delete(connId);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 获取当前统计信息
|
|
187
|
+
*/
|
|
188
|
+
getStats() {
|
|
189
|
+
return {
|
|
190
|
+
connectionRate: this.slideAndCount(this.connectionWindow),
|
|
191
|
+
globalMessageRate: this.slideAndCount(this.globalMessageWindow),
|
|
192
|
+
trackedConnections: this.connectionMessageWindows.size,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
exports.RateLimiterService = RateLimiterService;
|
|
197
|
+
exports.RateLimiterService = RateLimiterService = RateLimiterService_1 = __decorate([
|
|
198
|
+
(0, common_1.Injectable)(),
|
|
199
|
+
__metadata("design:paramtypes", [config_service_1.ConfigService])
|
|
200
|
+
], RateLimiterService);
|
|
201
|
+
//# sourceMappingURL=rate-limiter.service.js.map
|