@thejrsoft/subway-protocol 1.3.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/ACK_MESSAGES_IMPLEMENTATION_SUMMARY.md +128 -0
- package/ACK_MESSAGE_DESIGN.md +457 -0
- package/CHANGELOG.md +58 -0
- package/COMMAND_VALIDATION_RULES.md +178 -0
- package/DOCUMENTATION_REORGANIZATION_SUMMARY.md +81 -0
- package/DOCUMENTATION_STRUCTURE.md +106 -0
- package/GATEWAY_MIGRATION_GUIDE.md +130 -0
- package/GATEWAY_PROTOCOL_COMPARISON.md +216 -0
- package/INTEGRATION_GUIDE.md +190 -0
- package/OPTIONAL_FIELDS_WITHOUT_DEFAULTS.md +97 -0
- package/PROTOCOL_UTILS_USAGE.md +278 -0
- package/README.md +237 -0
- package/TYPE_FIXES_SUMMARY.md +210 -0
- package/UPDATE_ENUM_VALUES.md +139 -0
- package/dist/asyncapi-sync.d.ts +47 -0
- package/dist/asyncapi-sync.d.ts.map +1 -0
- package/dist/asyncapi-sync.js +85 -0
- package/dist/asyncapi-sync.js.map +1 -0
- package/dist/command-factory.d.ts +62 -0
- package/dist/command-factory.d.ts.map +1 -0
- package/dist/command-factory.js +137 -0
- package/dist/command-factory.js.map +1 -0
- package/dist/command-types.d.ts +27 -0
- package/dist/command-types.d.ts.map +1 -0
- package/dist/command-types.js +31 -0
- package/dist/command-types.js.map +1 -0
- package/dist/index.d.ts +403 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +413 -0
- package/dist/index.js.map +1 -0
- package/dist/message-validator.d.ts +102 -0
- package/dist/message-validator.d.ts.map +1 -0
- package/dist/message-validator.js +640 -0
- package/dist/message-validator.js.map +1 -0
- package/dist/protocol-utils.d.ts +108 -0
- package/dist/protocol-utils.d.ts.map +1 -0
- package/dist/protocol-utils.js +293 -0
- package/dist/protocol-utils.js.map +1 -0
- package/docs/01-protocol/README.md +45 -0
- package/docs/01-protocol/design-rationale.md +198 -0
- package/docs/01-protocol/message-types.md +669 -0
- package/docs/01-protocol/specification.md +1466 -0
- package/docs/02-commands/README.md +56 -0
- package/docs/02-commands/batch-command.md +435 -0
- package/docs/02-commands/complex-command.md +537 -0
- package/docs/02-commands/simple-command.md +332 -0
- package/docs/02-commands/typed-commands.md +362 -0
- package/docs/03-architecture/README.md +66 -0
- package/docs/03-architecture/device-protocol.md +430 -0
- package/docs/03-architecture/edge-proxy.md +727 -0
- package/docs/03-architecture/routing-flow.md +893 -0
- package/docs/04-integration/README.md +144 -0
- package/docs/04-integration/backend-guide.md +551 -0
- package/docs/04-integration/edge-guide.md +684 -0
- package/docs/04-integration/gateway-guide.md +180 -0
- package/docs/04-integration/migration-guide.md +226 -0
- package/docs/05-examples/README.md +141 -0
- package/docs/05-examples/progress-update-examples.md +757 -0
- package/docs/06-reference/README.md +67 -0
- package/docs/06-reference/api.md +572 -0
- package/docs/06-reference/faq.md +302 -0
- package/docs/06-reference/glossary.md +232 -0
- package/examples/backend-upgrade.ts +279 -0
- package/examples/edge-multi-device.ts +513 -0
- package/examples/gateway-upgrade.ts +150 -0
- package/examples/protocol-implementation.ts +715 -0
- package/package.json +48 -0
- package/scripts/validate-asyncapi.ts +78 -0
- package/src/__tests__/protocol.test.ts +297 -0
- package/src/asyncapi-sync.ts +84 -0
- package/src/command-factory.ts +183 -0
- package/src/command-types.ts +72 -0
- package/src/edge-proxy.ts +494 -0
- package/src/gateway-extensions.ts +278 -0
- package/src/index.ts +792 -0
- package/src/message-validator.ts +726 -0
- package/src/protocol-utils.ts +355 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# ACK 消息实现总结
|
|
2
|
+
|
|
3
|
+
## 已完成的更新
|
|
4
|
+
|
|
5
|
+
### 1. 接口定义补充
|
|
6
|
+
|
|
7
|
+
#### RegisterAckMessage 增强
|
|
8
|
+
```typescript
|
|
9
|
+
export interface RegisterAckMessage extends BaseMessage {
|
|
10
|
+
type: MessageType.REGISTER_ACK;
|
|
11
|
+
clientId: string; // 确认的客户端ID
|
|
12
|
+
success: boolean; // 注册是否成功
|
|
13
|
+
sessionId?: string; // 成功时分配的会话ID
|
|
14
|
+
error?: { // 失败时的错误信息
|
|
15
|
+
code: string;
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
serverInfo?: { // 服务器信息
|
|
19
|
+
version: string;
|
|
20
|
+
capabilities: string[];
|
|
21
|
+
currentLoad?: number; // 当前负载
|
|
22
|
+
maxClients?: number; // 最大客户端数
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
#### UnregisterMessage 新增
|
|
28
|
+
```typescript
|
|
29
|
+
export interface UnregisterMessage extends BaseMessage {
|
|
30
|
+
type: MessageType.UNREGISTER;
|
|
31
|
+
clientId: string; // 客户端唯一标识符
|
|
32
|
+
reason?: string; // 注销原因
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### UnregisterAckMessage 新增
|
|
37
|
+
```typescript
|
|
38
|
+
export interface UnregisterAckMessage extends BaseMessage {
|
|
39
|
+
type: MessageType.UNREGISTER_ACK;
|
|
40
|
+
clientId: string; // 客户端唯一标识符
|
|
41
|
+
success: boolean; // 注销是否成功
|
|
42
|
+
cleanupInfo?: { // 清理信息
|
|
43
|
+
messagesProcessed: number; // 已处理的消息数
|
|
44
|
+
pendingMessages: number; // 待处理的消息数
|
|
45
|
+
connectionDuration: number; // 连接持续时间(秒)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### HeartbeatMessage 增强
|
|
51
|
+
```typescript
|
|
52
|
+
export interface HeartbeatMessage extends BaseMessage {
|
|
53
|
+
type: MessageType.HEARTBEAT;
|
|
54
|
+
clientId: string; // 客户端唯一标识符
|
|
55
|
+
sequence: number; // 序列号,用于匹配请求和响应
|
|
56
|
+
clientTime: string; // 客户端时间戳
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### HeartbeatAckMessage 增强
|
|
61
|
+
```typescript
|
|
62
|
+
export interface HeartbeatAckMessage extends BaseMessage {
|
|
63
|
+
type: MessageType.HEARTBEAT_ACK;
|
|
64
|
+
clientId: string; // 客户端唯一标识符
|
|
65
|
+
sequence: number; // 原始序列号
|
|
66
|
+
clientTime: string; // 原始客户端时间
|
|
67
|
+
serverTime: string; // 服务器时间
|
|
68
|
+
latency?: number; // 服务器处理延迟(毫秒)
|
|
69
|
+
serverStatus?: { // 服务器状态
|
|
70
|
+
healthy: boolean;
|
|
71
|
+
activeConnections: number;
|
|
72
|
+
messageQueueSize: number;
|
|
73
|
+
cpuUsage?: number;
|
|
74
|
+
memoryUsage?: number;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 2. 类型守卫函数
|
|
80
|
+
|
|
81
|
+
新增了所有 ACK 消息的类型守卫函数:
|
|
82
|
+
- `isRegisterAckMessage()`
|
|
83
|
+
- `isUnregisterMessage()`
|
|
84
|
+
- `isUnregisterAckMessage()`
|
|
85
|
+
- `isHeartbeatMessage()`
|
|
86
|
+
- `isHeartbeatAckMessage()`
|
|
87
|
+
- `isErrorMessage()`
|
|
88
|
+
|
|
89
|
+
### 3. 工厂方法
|
|
90
|
+
|
|
91
|
+
- 更新了 `createHeartbeatMessage()` 以包含必需的 sequence 和 clientTime
|
|
92
|
+
- 新增了 `createUnregisterMessage()` 工厂方法
|
|
93
|
+
|
|
94
|
+
### 4. 导出类型
|
|
95
|
+
|
|
96
|
+
更新了 `AnyMessage` 联合类型,包含了所有新增的消息类型。
|
|
97
|
+
|
|
98
|
+
## 使用场景
|
|
99
|
+
|
|
100
|
+
### 1. 连接管理
|
|
101
|
+
- **注册流程**:客户端发送 REGISTER,Gateway 返回 REGISTER_ACK 确认状态和会话信息
|
|
102
|
+
- **注销流程**:客户端发送 UNREGISTER,Gateway 清理资源后返回 UNREGISTER_ACK
|
|
103
|
+
|
|
104
|
+
### 2. 健康监控
|
|
105
|
+
- **心跳检测**:通过 HEARTBEAT/HEARTBEAT_ACK 监控连接健康状态
|
|
106
|
+
- **延迟测量**:通过 sequence 和时间戳计算网络往返时间(RTT)
|
|
107
|
+
- **服务器状态**:HEARTBEAT_ACK 携带服务器负载和健康信息
|
|
108
|
+
|
|
109
|
+
### 3. 负载均衡
|
|
110
|
+
- **智能路由**:根据 REGISTER_ACK 中的 serverInfo.currentLoad 选择负载最低的服务器
|
|
111
|
+
- **容量管理**:通过 maxClients 了解服务器容量
|
|
112
|
+
|
|
113
|
+
### 4. 故障恢复
|
|
114
|
+
- **连接监控**:通过心跳 ACK 检测连接失效
|
|
115
|
+
- **优雅重连**:使用 UNREGISTER/UNREGISTER_ACK 确保资源正确清理
|
|
116
|
+
|
|
117
|
+
## 实现建议
|
|
118
|
+
|
|
119
|
+
1. **超时处理**:所有等待 ACK 的操作都应设置合理超时(建议 5 秒)
|
|
120
|
+
2. **序列号管理**:心跳消息使用递增序列号,便于匹配请求和响应
|
|
121
|
+
3. **错误重试**:注册失败时应实现指数退避重试
|
|
122
|
+
4. **资源清理**:确保超时或失败时清理待处理的 ACK 请求
|
|
123
|
+
|
|
124
|
+
## 文档位置
|
|
125
|
+
|
|
126
|
+
- 设计文档:`ACK_MESSAGE_DESIGN.md`
|
|
127
|
+
- 实现总结:`ACK_MESSAGES_IMPLEMENTATION_SUMMARY.md`
|
|
128
|
+
- 协议定义:`src/index.ts`
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# ACK 消息类型设计方案
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
ACK(Acknowledgment)消息用于确认接收方已成功接收并处理了发送方的消息。这对于保证消息可靠性和状态同步非常重要。
|
|
6
|
+
|
|
7
|
+
## ACK 消息类型定义
|
|
8
|
+
|
|
9
|
+
### 1. REGISTER_ACK - 注册确认
|
|
10
|
+
|
|
11
|
+
#### 使用场景
|
|
12
|
+
- 客户端发送 REGISTER 消息后,Gateway 返回 REGISTER_ACK 确认注册结果
|
|
13
|
+
- 用于通知客户端是否成功注册,以及分配的会话信息
|
|
14
|
+
|
|
15
|
+
#### 接口定义
|
|
16
|
+
```typescript
|
|
17
|
+
export interface RegisterAckMessage extends BaseMessage {
|
|
18
|
+
type: MessageType.REGISTER_ACK;
|
|
19
|
+
clientId: string; // 确认的客户端ID
|
|
20
|
+
success: boolean; // 注册是否成功
|
|
21
|
+
sessionId?: string; // 成功时分配的会话ID
|
|
22
|
+
error?: { // 失败时的错误信息
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
};
|
|
26
|
+
serverInfo?: { // 服务器信息
|
|
27
|
+
version: string;
|
|
28
|
+
capabilities: string[];
|
|
29
|
+
currentLoad?: number; // 当前负载
|
|
30
|
+
maxClients?: number; // 最大客户端数
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### 交互流程
|
|
36
|
+
```
|
|
37
|
+
Client Gateway
|
|
38
|
+
| |
|
|
39
|
+
| 1. REGISTER |
|
|
40
|
+
|----------------------->|
|
|
41
|
+
| | 2. 验证客户端信息
|
|
42
|
+
| | 3. 分配资源
|
|
43
|
+
| 4. REGISTER_ACK |
|
|
44
|
+
|<-----------------------|
|
|
45
|
+
| |
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. UNREGISTER_ACK - 注销确认
|
|
49
|
+
|
|
50
|
+
#### 使用场景
|
|
51
|
+
- 客户端主动断开连接前发送 UNREGISTER 消息
|
|
52
|
+
- Gateway 清理资源后返回 UNREGISTER_ACK 确认
|
|
53
|
+
|
|
54
|
+
#### 接口定义
|
|
55
|
+
```typescript
|
|
56
|
+
export interface UnregisterMessage extends BaseMessage {
|
|
57
|
+
type: MessageType.UNREGISTER;
|
|
58
|
+
clientId: string;
|
|
59
|
+
reason?: string; // 注销原因
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface UnregisterAckMessage extends BaseMessage {
|
|
63
|
+
type: MessageType.UNREGISTER_ACK;
|
|
64
|
+
clientId: string;
|
|
65
|
+
success: boolean;
|
|
66
|
+
cleanupInfo?: { // 清理信息
|
|
67
|
+
messagesProcessed: number; // 已处理的消息数
|
|
68
|
+
pendingMessages: number; // 待处理的消息数
|
|
69
|
+
connectionDuration: number; // 连接持续时间(秒)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### 交互流程
|
|
75
|
+
```
|
|
76
|
+
Client Gateway
|
|
77
|
+
| |
|
|
78
|
+
| 1. UNREGISTER |
|
|
79
|
+
|----------------------->|
|
|
80
|
+
| | 2. 停止接收新消息
|
|
81
|
+
| | 3. 处理待处理消息
|
|
82
|
+
| | 4. 清理资源
|
|
83
|
+
| 5. UNREGISTER_ACK |
|
|
84
|
+
|<-----------------------|
|
|
85
|
+
| 6. 关闭连接 |
|
|
86
|
+
|----------------------->|
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. HEARTBEAT_ACK - 心跳确认
|
|
90
|
+
|
|
91
|
+
#### 使用场景
|
|
92
|
+
- 用于精确测量网络延迟
|
|
93
|
+
- 确认双向连接正常
|
|
94
|
+
- 携带服务器状态信息
|
|
95
|
+
|
|
96
|
+
#### 接口定义
|
|
97
|
+
```typescript
|
|
98
|
+
export interface HeartbeatMessage extends BaseMessage {
|
|
99
|
+
type: MessageType.HEARTBEAT;
|
|
100
|
+
clientId: string;
|
|
101
|
+
sequence: number; // 序列号,用于匹配请求和响应
|
|
102
|
+
clientTime: string; // 客户端时间戳
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface HeartbeatAckMessage extends BaseMessage {
|
|
106
|
+
type: MessageType.HEARTBEAT_ACK;
|
|
107
|
+
clientId: string;
|
|
108
|
+
sequence: number; // 原始序列号
|
|
109
|
+
clientTime: string; // 原始客户端时间
|
|
110
|
+
serverTime: string; // 服务器时间
|
|
111
|
+
latency?: number; // 服务器处理延迟(毫秒)
|
|
112
|
+
serverStatus?: { // 服务器状态
|
|
113
|
+
healthy: boolean;
|
|
114
|
+
activeConnections: number;
|
|
115
|
+
messageQueueSize: number;
|
|
116
|
+
cpuUsage?: number;
|
|
117
|
+
memoryUsage?: number;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### 交互流程
|
|
123
|
+
```
|
|
124
|
+
Client Gateway
|
|
125
|
+
| |
|
|
126
|
+
| 1. HEARTBEAT |
|
|
127
|
+
| (seq=123) |
|
|
128
|
+
|----------------------->|
|
|
129
|
+
| | 2. 记录接收时间
|
|
130
|
+
| | 3. 检查系统状态
|
|
131
|
+
| 4. HEARTBEAT_ACK |
|
|
132
|
+
| (seq=123) |
|
|
133
|
+
|<-----------------------|
|
|
134
|
+
| 5. 计算往返延迟 |
|
|
135
|
+
| |
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 实现示例
|
|
139
|
+
|
|
140
|
+
### 1. Gateway 端实现
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
class GatewayAckHandler {
|
|
144
|
+
// 处理注册消息
|
|
145
|
+
async handleRegister(ws: WebSocket, msg: RegisterMessage): Promise<void> {
|
|
146
|
+
try {
|
|
147
|
+
// 验证客户端
|
|
148
|
+
const validation = await this.validateClient(msg);
|
|
149
|
+
|
|
150
|
+
if (validation.success) {
|
|
151
|
+
// 注册成功
|
|
152
|
+
const sessionId = this.generateSessionId();
|
|
153
|
+
this.registerClient(msg.clientId, sessionId, ws);
|
|
154
|
+
|
|
155
|
+
const ackMessage: RegisterAckMessage = {
|
|
156
|
+
type: MessageType.REGISTER_ACK,
|
|
157
|
+
clientId: msg.clientId,
|
|
158
|
+
success: true,
|
|
159
|
+
sessionId,
|
|
160
|
+
serverInfo: {
|
|
161
|
+
version: '1.0',
|
|
162
|
+
capabilities: ['command', 'batch', 'complex'],
|
|
163
|
+
currentLoad: this.getCurrentLoad(),
|
|
164
|
+
maxClients: this.maxClients
|
|
165
|
+
},
|
|
166
|
+
timestamp: new Date().toISOString(),
|
|
167
|
+
version: '1.0'
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
ws.send(JSON.stringify(ackMessage));
|
|
171
|
+
} else {
|
|
172
|
+
// 注册失败
|
|
173
|
+
const ackMessage: RegisterAckMessage = {
|
|
174
|
+
type: MessageType.REGISTER_ACK,
|
|
175
|
+
clientId: msg.clientId,
|
|
176
|
+
success: false,
|
|
177
|
+
error: {
|
|
178
|
+
code: validation.errorCode,
|
|
179
|
+
message: validation.errorMessage
|
|
180
|
+
},
|
|
181
|
+
timestamp: new Date().toISOString(),
|
|
182
|
+
version: '1.0'
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
ws.send(JSON.stringify(ackMessage));
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
// 处理异常
|
|
189
|
+
this.sendErrorAck(ws, msg.clientId, error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 处理心跳消息
|
|
194
|
+
handleHeartbeat(ws: WebSocket, msg: HeartbeatMessage): void {
|
|
195
|
+
const serverTime = new Date().toISOString();
|
|
196
|
+
const receiveTime = Date.now();
|
|
197
|
+
|
|
198
|
+
// 计算服务器处理延迟
|
|
199
|
+
const latency = receiveTime - new Date(msg.timestamp).getTime();
|
|
200
|
+
|
|
201
|
+
const ackMessage: HeartbeatAckMessage = {
|
|
202
|
+
type: MessageType.HEARTBEAT_ACK,
|
|
203
|
+
clientId: msg.clientId,
|
|
204
|
+
sequence: msg.sequence,
|
|
205
|
+
clientTime: msg.clientTime,
|
|
206
|
+
serverTime,
|
|
207
|
+
latency,
|
|
208
|
+
serverStatus: {
|
|
209
|
+
healthy: true,
|
|
210
|
+
activeConnections: this.getActiveConnections(),
|
|
211
|
+
messageQueueSize: this.getQueueSize(),
|
|
212
|
+
cpuUsage: this.getCpuUsage(),
|
|
213
|
+
memoryUsage: this.getMemoryUsage()
|
|
214
|
+
},
|
|
215
|
+
timestamp: serverTime,
|
|
216
|
+
version: '1.0'
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
ws.send(JSON.stringify(ackMessage));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 2. 客户端实现
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
class ClientAckHandler {
|
|
228
|
+
private pendingAcks = new Map<string, PendingAck>();
|
|
229
|
+
|
|
230
|
+
// 发送注册消息并等待 ACK
|
|
231
|
+
async register(): Promise<RegisterAckMessage> {
|
|
232
|
+
const registerMsg: RegisterMessage = {
|
|
233
|
+
type: MessageType.REGISTER,
|
|
234
|
+
clientId: this.clientId,
|
|
235
|
+
clientType: this.clientType,
|
|
236
|
+
clientInfo: {
|
|
237
|
+
version: '1.0.0',
|
|
238
|
+
platform: 'node',
|
|
239
|
+
capabilities: ['command', 'batch']
|
|
240
|
+
},
|
|
241
|
+
timestamp: new Date().toISOString(),
|
|
242
|
+
version: '1.0'
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// 发送并等待 ACK
|
|
246
|
+
return await this.sendAndWaitForAck(
|
|
247
|
+
registerMsg,
|
|
248
|
+
MessageType.REGISTER_ACK,
|
|
249
|
+
5000 // 5秒超时
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// 发送心跳并测量延迟
|
|
254
|
+
async sendHeartbeat(): Promise<number> {
|
|
255
|
+
const sequence = this.nextSequence++;
|
|
256
|
+
const clientTime = new Date().toISOString();
|
|
257
|
+
const sendTime = Date.now();
|
|
258
|
+
|
|
259
|
+
const heartbeatMsg: HeartbeatMessage = {
|
|
260
|
+
type: MessageType.HEARTBEAT,
|
|
261
|
+
clientId: this.clientId,
|
|
262
|
+
sequence,
|
|
263
|
+
clientTime,
|
|
264
|
+
timestamp: clientTime,
|
|
265
|
+
version: '1.0'
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const ack = await this.sendAndWaitForAck(
|
|
269
|
+
heartbeatMsg,
|
|
270
|
+
MessageType.HEARTBEAT_ACK,
|
|
271
|
+
3000
|
|
272
|
+
) as HeartbeatAckMessage;
|
|
273
|
+
|
|
274
|
+
// 计算往返时间
|
|
275
|
+
const rtt = Date.now() - sendTime;
|
|
276
|
+
|
|
277
|
+
// 更新服务器状态
|
|
278
|
+
this.updateServerStatus(ack.serverStatus);
|
|
279
|
+
|
|
280
|
+
return rtt;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 优雅断开连接
|
|
284
|
+
async disconnect(): Promise<void> {
|
|
285
|
+
const unregisterMsg: UnregisterMessage = {
|
|
286
|
+
type: MessageType.UNREGISTER,
|
|
287
|
+
clientId: this.clientId,
|
|
288
|
+
reason: 'Client shutdown',
|
|
289
|
+
timestamp: new Date().toISOString(),
|
|
290
|
+
version: '1.0'
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
const ack = await this.sendAndWaitForAck(
|
|
295
|
+
unregisterMsg,
|
|
296
|
+
MessageType.UNREGISTER_ACK,
|
|
297
|
+
5000
|
|
298
|
+
) as UnregisterAckMessage;
|
|
299
|
+
|
|
300
|
+
console.log('Disconnected gracefully:', ack.cleanupInfo);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
console.error('Disconnect error:', error);
|
|
303
|
+
} finally {
|
|
304
|
+
this.ws.close();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## 使用场景详解
|
|
311
|
+
|
|
312
|
+
### 1. 连接建立流程
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// 客户端
|
|
316
|
+
async function connectToGateway() {
|
|
317
|
+
const client = new WebSocketClient('ws://gateway:18081');
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
// 1. 建立 WebSocket 连接
|
|
321
|
+
await client.connect();
|
|
322
|
+
|
|
323
|
+
// 2. 发送注册消息并等待确认
|
|
324
|
+
const registerAck = await client.register();
|
|
325
|
+
|
|
326
|
+
if (!registerAck.success) {
|
|
327
|
+
throw new Error(`Registration failed: ${registerAck.error.message}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 3. 保存会话信息
|
|
331
|
+
client.sessionId = registerAck.sessionId;
|
|
332
|
+
console.log(`Connected with session: ${registerAck.sessionId}`);
|
|
333
|
+
|
|
334
|
+
// 4. 启动心跳
|
|
335
|
+
client.startHeartbeat();
|
|
336
|
+
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error('Connection failed:', error);
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### 2. 网络质量监控
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
class NetworkMonitor {
|
|
348
|
+
private rttHistory: number[] = [];
|
|
349
|
+
private readonly maxHistory = 100;
|
|
350
|
+
|
|
351
|
+
async monitorNetwork(client: WebSocketClient): Promise<void> {
|
|
352
|
+
setInterval(async () => {
|
|
353
|
+
try {
|
|
354
|
+
const rtt = await client.sendHeartbeat();
|
|
355
|
+
this.rttHistory.push(rtt);
|
|
356
|
+
|
|
357
|
+
if (this.rttHistory.length > this.maxHistory) {
|
|
358
|
+
this.rttHistory.shift();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// 分析网络质量
|
|
362
|
+
const avgRtt = this.calculateAverage(this.rttHistory);
|
|
363
|
+
const jitter = this.calculateJitter(this.rttHistory);
|
|
364
|
+
|
|
365
|
+
if (avgRtt > 1000 || jitter > 200) {
|
|
366
|
+
console.warn('Poor network quality detected', {
|
|
367
|
+
averageRtt: avgRtt,
|
|
368
|
+
jitter: jitter
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error('Heartbeat failed:', error);
|
|
373
|
+
// 可能需要重连
|
|
374
|
+
}
|
|
375
|
+
}, 30000); // 30秒一次
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 3. 负载均衡决策
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
class LoadBalancer {
|
|
384
|
+
private gateways: GatewayInfo[] = [];
|
|
385
|
+
|
|
386
|
+
// 根据 REGISTER_ACK 中的服务器信息选择最优网关
|
|
387
|
+
selectBestGateway(): GatewayInfo {
|
|
388
|
+
// 收集所有网关的负载信息
|
|
389
|
+
const availableGateways = this.gateways.filter(gw => {
|
|
390
|
+
return gw.lastAck && gw.lastAck.serverInfo;
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// 选择负载最低的网关
|
|
394
|
+
return availableGateways.reduce((best, current) => {
|
|
395
|
+
const bestLoad = best.lastAck.serverInfo.currentLoad || 0;
|
|
396
|
+
const currentLoad = current.lastAck.serverInfo.currentLoad || 0;
|
|
397
|
+
return currentLoad < bestLoad ? current : best;
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 4. 故障检测和恢复
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
class ConnectionManager {
|
|
407
|
+
private missedHeartbeats = 0;
|
|
408
|
+
private readonly maxMissedHeartbeats = 3;
|
|
409
|
+
|
|
410
|
+
async checkConnection(): Promise<void> {
|
|
411
|
+
try {
|
|
412
|
+
await this.client.sendHeartbeat();
|
|
413
|
+
this.missedHeartbeats = 0;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
this.missedHeartbeats++;
|
|
416
|
+
|
|
417
|
+
if (this.missedHeartbeats >= this.maxMissedHeartbeats) {
|
|
418
|
+
console.error('Connection lost, attempting reconnect...');
|
|
419
|
+
await this.reconnect();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async reconnect(): Promise<void> {
|
|
425
|
+
// 1. 关闭现有连接
|
|
426
|
+
this.client.close();
|
|
427
|
+
|
|
428
|
+
// 2. 等待一段时间
|
|
429
|
+
await this.delay(5000);
|
|
430
|
+
|
|
431
|
+
// 3. 重新连接
|
|
432
|
+
await this.client.connect();
|
|
433
|
+
|
|
434
|
+
// 4. 重新注册
|
|
435
|
+
const ack = await this.client.register();
|
|
436
|
+
if (ack.success) {
|
|
437
|
+
console.log('Reconnected successfully');
|
|
438
|
+
this.missedHeartbeats = 0;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## 优势
|
|
445
|
+
|
|
446
|
+
1. **可靠性保证** - 确保消息被正确接收和处理
|
|
447
|
+
2. **状态同步** - 客户端和服务器保持状态一致
|
|
448
|
+
3. **性能监控** - 通过心跳 ACK 监控网络延迟
|
|
449
|
+
4. **优雅关闭** - 确保资源正确清理
|
|
450
|
+
5. **错误处理** - 明确的错误反馈机制
|
|
451
|
+
|
|
452
|
+
## 注意事项
|
|
453
|
+
|
|
454
|
+
1. **超时处理** - 所有等待 ACK 的操作都应设置合理的超时时间
|
|
455
|
+
2. **重试机制** - 对于关键操作(如注册),应实现重试逻辑
|
|
456
|
+
3. **资源清理** - 超时或失败时要清理待处理的 ACK 请求
|
|
457
|
+
4. **性能影响** - ACK 机制会增加消息往返,需要权衡可靠性和性能
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.0.1] - 2024-01-XX
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- 为 `CommandMessage` 的 `retryCount` 字段添加默认值 0
|
|
7
|
+
- 为 `createCommandResponseMessage` 添加自动计算 `executionTime` 功能
|
|
8
|
+
- 新增 `commandStartTime` 选项,可自动计算命令执行时间
|
|
9
|
+
- 为 `createProgramResponseMessage` 添加自动计算 `executionTime` 功能
|
|
10
|
+
- 新增 `programStartTime` 选项,可自动计算程序执行时间
|
|
11
|
+
- 为 `createHeartbeatAckMessage` 添加自动计算 `latency` 功能
|
|
12
|
+
- 新增 `heartbeatReceivedTime` 选项,可自动计算处理延迟
|
|
13
|
+
|
|
14
|
+
### 使用示例
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// 1. retryCount 现在有默认值 0
|
|
18
|
+
const command = MessageFactory.createCommandMessage(
|
|
19
|
+
requestRef,
|
|
20
|
+
targetClientId,
|
|
21
|
+
commandObj,
|
|
22
|
+
callbackUrl
|
|
23
|
+
// retryCount 将默认为 0
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// 2. 自动计算 executionTime
|
|
27
|
+
const startTime = Date.now();
|
|
28
|
+
// ... 执行命令 ...
|
|
29
|
+
const response = MessageFactory.createCommandResponseMessage(
|
|
30
|
+
requestRef,
|
|
31
|
+
CommandStatus.COMPLETED,
|
|
32
|
+
result,
|
|
33
|
+
'Success',
|
|
34
|
+
{ commandStartTime: startTime } // executionTime 将自动计算
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// 3. 自动计算 latency
|
|
38
|
+
const receivedTime = Date.now();
|
|
39
|
+
// ... 处理心跳 ...
|
|
40
|
+
const ack = MessageFactory.createHeartbeatAckMessage(
|
|
41
|
+
sequence,
|
|
42
|
+
clientId,
|
|
43
|
+
clientTime,
|
|
44
|
+
{ heartbeatReceivedTime: receivedTime } // latency 将自动计算
|
|
45
|
+
);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
- 无破坏性更改,所有改动向后兼容
|
|
50
|
+
|
|
51
|
+
## [1.0.0] - 2024-01-XX
|
|
52
|
+
|
|
53
|
+
### Initial Release
|
|
54
|
+
- 统一的 WebSocket 协议定义
|
|
55
|
+
- 完整的消息类型系统
|
|
56
|
+
- MessageFactory 工具类
|
|
57
|
+
- MessageValidator 验证器
|
|
58
|
+
- ProtocolUtils 工具类
|