@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,206 @@
|
|
|
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 SessionService_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.SessionService = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const redis_service_1 = require("../redis/redis.service");
|
|
16
|
+
const core_1 = require("@roomkit/core");
|
|
17
|
+
const uuid_1 = require("uuid");
|
|
18
|
+
/**
|
|
19
|
+
* 会话管理服务
|
|
20
|
+
*
|
|
21
|
+
* 负责管理用户会话信息,支持断线重连
|
|
22
|
+
* - 会话存储在 Redis 中
|
|
23
|
+
* - 使用 TTL 自动过期(默认 5 分钟)
|
|
24
|
+
*/
|
|
25
|
+
let SessionService = SessionService_1 = class SessionService {
|
|
26
|
+
redisService;
|
|
27
|
+
logger = new common_1.Logger(SessionService_1.name);
|
|
28
|
+
/** 会话过期时间(秒) */
|
|
29
|
+
SESSION_TTL = 5 * 60; // 5分钟
|
|
30
|
+
constructor(redisService) {
|
|
31
|
+
this.redisService = redisService;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 创建或更新用户会话
|
|
35
|
+
*/
|
|
36
|
+
async createOrUpdateSession(userId, options) {
|
|
37
|
+
const redis = this.redisService.client;
|
|
38
|
+
// 尝试获取现有会话
|
|
39
|
+
let existingSession = await this.getSessionByUserId(userId);
|
|
40
|
+
let sessionToken;
|
|
41
|
+
if (existingSession) {
|
|
42
|
+
// 更新现有会话
|
|
43
|
+
sessionToken = existingSession.sessionToken;
|
|
44
|
+
const session = {
|
|
45
|
+
...existingSession,
|
|
46
|
+
gatewayId: options?.gatewayId ?? existingSession.gatewayId,
|
|
47
|
+
connectionId: options?.connectionId ?? existingSession.connectionId,
|
|
48
|
+
rooms: options?.rooms ?? existingSession.rooms,
|
|
49
|
+
lastActivity: Date.now(),
|
|
50
|
+
};
|
|
51
|
+
await this.saveSession(session);
|
|
52
|
+
this.logger.debug(`Updated session for user ${userId}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// 创建新会话
|
|
56
|
+
sessionToken = (0, uuid_1.v4)();
|
|
57
|
+
const session = {
|
|
58
|
+
userId,
|
|
59
|
+
sessionToken,
|
|
60
|
+
gatewayId: options?.gatewayId,
|
|
61
|
+
connectionId: options?.connectionId,
|
|
62
|
+
rooms: options?.rooms || [],
|
|
63
|
+
lastActivity: Date.now(),
|
|
64
|
+
createdAt: Date.now(),
|
|
65
|
+
};
|
|
66
|
+
await this.saveSession(session);
|
|
67
|
+
this.logger.log(`Created new session for user ${userId}`);
|
|
68
|
+
}
|
|
69
|
+
return sessionToken;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 保存会话到 Redis(带 TTL)
|
|
73
|
+
*/
|
|
74
|
+
async saveSession(session) {
|
|
75
|
+
const redis = this.redisService.client;
|
|
76
|
+
const userSessionKey = core_1.RedisKeys.userSession(session.userId);
|
|
77
|
+
const tokenKey = core_1.RedisKeys.sessionToken(session.sessionToken);
|
|
78
|
+
// 保存用户会话信息
|
|
79
|
+
await redis.setex(userSessionKey, this.SESSION_TTL, JSON.stringify(session));
|
|
80
|
+
// 保存令牌到用户ID的映射(用于快速查找)
|
|
81
|
+
await redis.setex(tokenKey, this.SESSION_TTL, session.userId);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 根据令牌获取会话
|
|
85
|
+
*/
|
|
86
|
+
async getSessionByToken(token) {
|
|
87
|
+
const redis = this.redisService.client;
|
|
88
|
+
const tokenKey = core_1.RedisKeys.sessionToken(token);
|
|
89
|
+
// 先通过令牌找到 userId
|
|
90
|
+
const userId = await redis.get(tokenKey);
|
|
91
|
+
if (!userId) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
// 再获取完整会话信息
|
|
95
|
+
return this.getSessionByUserId(userId);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 根据用户ID获取会话
|
|
99
|
+
*/
|
|
100
|
+
async getSessionByUserId(userId) {
|
|
101
|
+
const redis = this.redisService.client;
|
|
102
|
+
const userSessionKey = core_1.RedisKeys.userSession(userId);
|
|
103
|
+
const data = await redis.get(userSessionKey);
|
|
104
|
+
if (!data) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(data);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
this.logger.error(`Failed to parse session for user ${userId}:`, error);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 添加房间到会话(包含房间类型)
|
|
117
|
+
*/
|
|
118
|
+
async addRoomToSession(userId, roomId, gameType) {
|
|
119
|
+
const session = await this.getSessionByUserId(userId);
|
|
120
|
+
if (!session) {
|
|
121
|
+
this.logger.warn(`Session not found for user ${userId}`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!session.rooms.find(r => r.roomId === roomId)) {
|
|
125
|
+
session.rooms.push({ roomId, gameType });
|
|
126
|
+
session.lastActivity = Date.now();
|
|
127
|
+
await this.saveSession(session);
|
|
128
|
+
this.logger.debug(`Added room ${roomId} (${gameType}) to session for user ${userId}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 从会话中移除房间
|
|
133
|
+
*/
|
|
134
|
+
async removeRoomFromSession(userId, roomId) {
|
|
135
|
+
const session = await this.getSessionByUserId(userId);
|
|
136
|
+
if (!session) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
session.rooms = session.rooms.filter(r => r.roomId !== roomId);
|
|
140
|
+
session.lastActivity = Date.now();
|
|
141
|
+
await this.saveSession(session);
|
|
142
|
+
this.logger.debug(`Removed room ${roomId} from session for user ${userId}`);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* 更新会话的连接信息
|
|
146
|
+
*/
|
|
147
|
+
async updateConnectionInfo(userId, gatewayId, connectionId) {
|
|
148
|
+
const session = await this.getSessionByUserId(userId);
|
|
149
|
+
if (!session) {
|
|
150
|
+
this.logger.warn(`Session not found for user ${userId}`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
session.gatewayId = gatewayId;
|
|
154
|
+
session.connectionId = connectionId;
|
|
155
|
+
session.lastActivity = Date.now();
|
|
156
|
+
await this.saveSession(session);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 刷新会话的 TTL(智能刷新)
|
|
160
|
+
*
|
|
161
|
+
* 只有当 TTL 剩余时间少于阈值时才刷新,避免频繁写入
|
|
162
|
+
* @param userId 用户ID
|
|
163
|
+
* @param forceRefresh 是否强制刷新(默认false)
|
|
164
|
+
*/
|
|
165
|
+
async refreshSession(userId, forceRefresh = false) {
|
|
166
|
+
const redis = this.redisService.client;
|
|
167
|
+
const userSessionKey = core_1.RedisKeys.userSession(userId);
|
|
168
|
+
if (!forceRefresh) {
|
|
169
|
+
// 检查剩余 TTL
|
|
170
|
+
const ttl = await redis.ttl(userSessionKey);
|
|
171
|
+
// 如果 TTL 大于 2 分钟,不需要刷新
|
|
172
|
+
if (ttl > 120) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// 如果 TTL 为 -2(key不存在)或 -1(无过期时间),不刷新
|
|
176
|
+
if (ttl < 0) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// 获取并刷新 session
|
|
181
|
+
const session = await this.getSessionByUserId(userId);
|
|
182
|
+
if (session) {
|
|
183
|
+
session.lastActivity = Date.now();
|
|
184
|
+
await this.saveSession(session);
|
|
185
|
+
this.logger.debug(`Refreshed session for user ${userId}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* 删除会话
|
|
190
|
+
*/
|
|
191
|
+
async deleteSession(userId) {
|
|
192
|
+
const redis = this.redisService.client;
|
|
193
|
+
const session = await this.getSessionByUserId(userId);
|
|
194
|
+
if (session) {
|
|
195
|
+
await redis.del(core_1.RedisKeys.userSession(userId));
|
|
196
|
+
await redis.del(core_1.RedisKeys.sessionToken(session.sessionToken));
|
|
197
|
+
this.logger.debug(`Deleted session for user ${userId}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
exports.SessionService = SessionService;
|
|
202
|
+
exports.SessionService = SessionService = SessionService_1 = __decorate([
|
|
203
|
+
(0, common_1.Injectable)(),
|
|
204
|
+
__metadata("design:paramtypes", [redis_service_1.RedisService])
|
|
205
|
+
], SessionService);
|
|
206
|
+
//# sourceMappingURL=session.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.service.js","sourceRoot":"","sources":["../../../src/session/session.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ClientMessage, Connection } from '@roomkit/core';
|
|
2
|
+
import { ConfigService } from '../config/config.service';
|
|
3
|
+
import { RedisService } from '../redis/redis.service';
|
|
4
|
+
import { ConnectionService } from '../connection/connection.service';
|
|
5
|
+
import { SessionService } from '../session/session.service';
|
|
6
|
+
export declare class MessageRouterService {
|
|
7
|
+
private readonly configService;
|
|
8
|
+
private readonly redisService;
|
|
9
|
+
private readonly connectionService;
|
|
10
|
+
private readonly sessionService;
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private roomRoutes;
|
|
13
|
+
constructor(configService: ConfigService, redisService: RedisService, connectionService: ConnectionService, sessionService: SessionService);
|
|
14
|
+
/**
|
|
15
|
+
* 初始化路由更新监听
|
|
16
|
+
*/
|
|
17
|
+
private initializeRouteUpdateListener;
|
|
18
|
+
/**
|
|
19
|
+
* 处理路由更新
|
|
20
|
+
*/
|
|
21
|
+
private handleRouteUpdate;
|
|
22
|
+
/**
|
|
23
|
+
* 处理客户端消息
|
|
24
|
+
* 使用字符串消息类型格式:{ type: 'gw:auth', payload: {...} }
|
|
25
|
+
*/
|
|
26
|
+
handleMessage(connection: Connection, message: ClientMessage): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* 发送错误消息
|
|
29
|
+
*/
|
|
30
|
+
private sendErrorByType;
|
|
31
|
+
/**
|
|
32
|
+
* 转发消息到 Worker
|
|
33
|
+
*/
|
|
34
|
+
private forwardToWorkerByType;
|
|
35
|
+
/**
|
|
36
|
+
* 处理鉴权请求
|
|
37
|
+
*/
|
|
38
|
+
private handleAuthRequest;
|
|
39
|
+
/**
|
|
40
|
+
* 处理房间列表请求
|
|
41
|
+
*/
|
|
42
|
+
private handleRoomList;
|
|
43
|
+
/**
|
|
44
|
+
* 处理重连请求
|
|
45
|
+
*
|
|
46
|
+
* 统一的重连处理策略:
|
|
47
|
+
*
|
|
48
|
+
* **客户端视角**:
|
|
49
|
+
* - 客户端只知道 WebSocket 断开了
|
|
50
|
+
* - 无法区分是自己网络问题还是 Gateway 重启
|
|
51
|
+
* - 只需发送 sessionToken,不需要指定重连类型
|
|
52
|
+
*
|
|
53
|
+
* **服务端处理**:
|
|
54
|
+
* - Gateway 收到重连请求时,内存中已无旧 Connection 对象
|
|
55
|
+
* - 无论是客户端断连还是 Gateway 重启,处理流程相同:
|
|
56
|
+
* 1. 恢复 Gateway 本地连接映射
|
|
57
|
+
* 2. 通知 Worker 更新连接信息(Gateway ID + Connection ID)
|
|
58
|
+
* 3. 不发送房间状态(客户端已有状态)
|
|
59
|
+
*
|
|
60
|
+
* **Worker 重启场景**:
|
|
61
|
+
* - 不通过重连机制处理
|
|
62
|
+
* - Worker 应该有独立的容错恢复机制:
|
|
63
|
+
* - 启动时从 Redis/持久化恢复房间状态
|
|
64
|
+
* - 或延迟加载:收到消息时发现房间不存在,再恢复
|
|
65
|
+
* - 对客户端和 Gateway 透明
|
|
66
|
+
*/
|
|
67
|
+
private handleReconnect;
|
|
68
|
+
/**
|
|
69
|
+
* 创建房间
|
|
70
|
+
*/
|
|
71
|
+
private handleCreateRoom;
|
|
72
|
+
/**
|
|
73
|
+
* 加入房间
|
|
74
|
+
*/
|
|
75
|
+
private handleJoinRoom;
|
|
76
|
+
/**
|
|
77
|
+
* 离开房间
|
|
78
|
+
*
|
|
79
|
+
* 多房间模式:需要指定要离开的 roomId
|
|
80
|
+
* 单房间兼容:如果只在一个房间,可以不指定
|
|
81
|
+
*/
|
|
82
|
+
private handleLeaveRoom;
|
|
83
|
+
/**
|
|
84
|
+
* 转发消息到Worker
|
|
85
|
+
*
|
|
86
|
+
* @param connection 连接
|
|
87
|
+
* @param msgType 消息类型
|
|
88
|
+
* @param payload 消息内容
|
|
89
|
+
* @param roomId 目标房间ID
|
|
90
|
+
*/
|
|
91
|
+
private forwardToWorker;
|
|
92
|
+
/**
|
|
93
|
+
* 选择处理鉴权的Worker
|
|
94
|
+
*/
|
|
95
|
+
private selectAuthWorker;
|
|
96
|
+
/**
|
|
97
|
+
* 根据游戏类型选择Worker
|
|
98
|
+
*/
|
|
99
|
+
private selectWorker;
|
|
100
|
+
/**
|
|
101
|
+
* 获取所有健康的Worker
|
|
102
|
+
*/
|
|
103
|
+
private getHealthyWorkers;
|
|
104
|
+
/**
|
|
105
|
+
* 过滤出健康的Worker(心跳未超时且非draining状态)
|
|
106
|
+
*/
|
|
107
|
+
private filterHealthyWorkers;
|
|
108
|
+
/**
|
|
109
|
+
* 加入或创建房间(Colyseus 风格的 concurrentJoinOrCreateRoomLock)
|
|
110
|
+
*
|
|
111
|
+
* 实现原理:
|
|
112
|
+
* 1. 使用 HINCRBY 原子递增计数器,返回值为 1 的是第一个请求(负责创建)
|
|
113
|
+
* 2. 其他请求(返回值 > 1)等待创建完成
|
|
114
|
+
* 3. 创建完成后,Worker 广播通知,所有等待者自动加入
|
|
115
|
+
* 4. 所有请求都发送到同一个 Worker,由 Worker 统一处理
|
|
116
|
+
*
|
|
117
|
+
* @param connection 客户端连接
|
|
118
|
+
* @param payload 请求载荷
|
|
119
|
+
* @param token 可选的认证令牌(用于 Room.onAuth)
|
|
120
|
+
*/
|
|
121
|
+
private handleJoinOrCreate;
|
|
122
|
+
/**
|
|
123
|
+
* 转发加入请求(用于房间已存在时的 joinOrCreate)
|
|
124
|
+
*
|
|
125
|
+
* @param connection 客户端连接
|
|
126
|
+
* @param roomId 房间ID
|
|
127
|
+
* @param workerId 目标Worker ID
|
|
128
|
+
* @param isJoinOrCreate 是否为 joinOrCreate 操作
|
|
129
|
+
* @param token 可选的认证令牌
|
|
130
|
+
*/
|
|
131
|
+
private forwardJoinRequest;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=message-router.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-router.service.d.ts","sourceRoot":"","sources":["../../../src/ws/message-router.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,aAAa,EAMb,UAAU,EAGX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,qBACa,oBAAoB;IAO7B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc;IATjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAGhE,OAAO,CAAC,UAAU,CAA6B;gBAG5B,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,YAAY,EAC1B,iBAAiB,EAAE,iBAAiB,EACpC,cAAc,EAAE,cAAc;IAMjD;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAcrC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgBzB;;;OAGG;IACG,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAyFlF;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;YACW,qBAAqB;IA0CnC;;OAEG;YACW,iBAAiB;IAiD/B;;OAEG;YACW,cAAc;IAO5B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;YACW,eAAe;IAmH7B;;OAEG;YACW,gBAAgB;IAyD9B;;OAEG;YACW,cAAc;IAsC5B;;;;;OAKG;YACW,eAAe;IAyC7B;;;;;;;OAOG;YACW,eAAe;IA4B7B;;OAEG;YACW,gBAAgB;IAM9B;;OAEG;YACW,YAAY;IAkB1B;;OAEG;YACW,iBAAiB;IAK/B;;OAEG;YACW,oBAAoB;IA+BlC;;;;;;;;;;;;OAYG;YACW,kBAAkB;IA6GhC;;;;;;;;OAQG;YACW,kBAAkB;CAuBjC"}
|