magic-chat-im 2.9.2 → 2.9.3
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/es/common.d.ts +86 -0
- package/es/common.js +92 -0
- package/es/connection-manager.d.ts +83 -0
- package/es/connection-manager.js +434 -0
- package/es/error-handler.d.ts +45 -0
- package/es/error-handler.js +111 -0
- package/es/event-handler.d.ts +14 -0
- package/es/event-handler.js +215 -0
- package/es/heartbeat-manager.d.ts +37 -0
- package/es/heartbeat-manager.js +93 -0
- package/es/im.d.ts +162 -0
- package/es/im.interface.d.ts +234 -0
- package/es/im.interface.js +1 -0
- package/es/im.js +444 -0
- package/es/index.d.ts +7 -0
- package/es/index.js +8 -0
- package/es/logger.d.ts +98 -0
- package/es/logger.js +238 -0
- package/es/message-queue-manager.d.ts +45 -0
- package/es/message-queue-manager.js +128 -0
- package/es/message-sender.d.ts +26 -0
- package/es/message-sender.js +113 -0
- package/es/monitoring-manager.d.ts +48 -0
- package/es/monitoring-manager.js +161 -0
- package/es/reconnect.d.ts +19 -0
- package/es/reconnect.js +279 -0
- package/es/socket.d.ts +29 -0
- package/es/socket.js +163 -0
- package/es/types.d.ts +82 -0
- package/es/types.js +1 -0
- package/es/util.d.ts +11 -0
- package/es/util.js +44 -0
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { IMInstance, IMConnectionStats } from './im.interface';
|
|
2
|
+
export declare class MonitoringManager {
|
|
3
|
+
private im;
|
|
4
|
+
constructor(im: IMInstance);
|
|
5
|
+
/**
|
|
6
|
+
* 初始化连接统计信息
|
|
7
|
+
*/
|
|
8
|
+
initializeConnectionStats(): IMConnectionStats;
|
|
9
|
+
/**
|
|
10
|
+
* 更新连接打开统计
|
|
11
|
+
*/
|
|
12
|
+
updateConnectionOpenStats(): void;
|
|
13
|
+
/**
|
|
14
|
+
* 更新心跳超时统计
|
|
15
|
+
*/
|
|
16
|
+
updateHeartbeatTimeoutStats(): void;
|
|
17
|
+
/**
|
|
18
|
+
* 更新消息接收统计
|
|
19
|
+
*/
|
|
20
|
+
updateMessageReceivedStats(): void;
|
|
21
|
+
/**
|
|
22
|
+
* 更新消息发送统计
|
|
23
|
+
*/
|
|
24
|
+
updateMessageSentStats(): void;
|
|
25
|
+
/**
|
|
26
|
+
* 更新连接关闭统计
|
|
27
|
+
*/
|
|
28
|
+
updateConnectionCloseStats(): void;
|
|
29
|
+
/**
|
|
30
|
+
* 获取连接统计信息(用于监控)
|
|
31
|
+
* @returns {object} 连接统计信息
|
|
32
|
+
*/
|
|
33
|
+
getConnectionStats(): IMConnectionStats;
|
|
34
|
+
/**
|
|
35
|
+
* 获取性能指标(用于监控)
|
|
36
|
+
* @returns {object} 性能指标
|
|
37
|
+
*/
|
|
38
|
+
getPerformanceMetrics(): {
|
|
39
|
+
connectionSuccessRate: number;
|
|
40
|
+
messageLossRate: number;
|
|
41
|
+
averageReconnectionTime: number;
|
|
42
|
+
heartbeatTimeoutRate: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* 重置连接统计信息
|
|
46
|
+
*/
|
|
47
|
+
resetConnectionStats(): void;
|
|
48
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
5
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
6
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
7
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
8
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
9
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
10
|
+
export var MonitoringManager = /*#__PURE__*/function () {
|
|
11
|
+
function MonitoringManager(im) {
|
|
12
|
+
_classCallCheck(this, MonitoringManager);
|
|
13
|
+
_defineProperty(this, "im", void 0);
|
|
14
|
+
this.im = im;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 初始化连接统计信息
|
|
19
|
+
*/
|
|
20
|
+
_createClass(MonitoringManager, [{
|
|
21
|
+
key: "initializeConnectionStats",
|
|
22
|
+
value: function initializeConnectionStats() {
|
|
23
|
+
return {
|
|
24
|
+
totalConnections: 0,
|
|
25
|
+
totalReconnections: 0,
|
|
26
|
+
totalMessagesSent: 0,
|
|
27
|
+
totalMessagesReceived: 0,
|
|
28
|
+
totalHeartbeatTimeouts: 0,
|
|
29
|
+
firstConnectionTime: 0,
|
|
30
|
+
lastConnectTime: 0,
|
|
31
|
+
lastDisconnectTime: 0,
|
|
32
|
+
totalOnlineTime: 0,
|
|
33
|
+
currentOnlineTime: 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 更新连接打开统计
|
|
39
|
+
*/
|
|
40
|
+
}, {
|
|
41
|
+
key: "updateConnectionOpenStats",
|
|
42
|
+
value: function updateConnectionOpenStats() {
|
|
43
|
+
// 更新连接统计信息
|
|
44
|
+
this.im._connectionStats.totalConnections++;
|
|
45
|
+
var now = Date.now();
|
|
46
|
+
if (this.im._connectionStats.firstConnectionTime === 0) {
|
|
47
|
+
this.im._connectionStats.firstConnectionTime = now;
|
|
48
|
+
}
|
|
49
|
+
this.im._connectionStats.lastConnectTime = now;
|
|
50
|
+
if (this.im._connectionStats.totalConnections > 1) {
|
|
51
|
+
this.im._connectionStats.totalReconnections++;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 更新心跳超时统计
|
|
57
|
+
*/
|
|
58
|
+
}, {
|
|
59
|
+
key: "updateHeartbeatTimeoutStats",
|
|
60
|
+
value: function updateHeartbeatTimeoutStats() {
|
|
61
|
+
this.im._connectionStats.totalHeartbeatTimeouts++;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 更新消息接收统计
|
|
66
|
+
*/
|
|
67
|
+
}, {
|
|
68
|
+
key: "updateMessageReceivedStats",
|
|
69
|
+
value: function updateMessageReceivedStats() {
|
|
70
|
+
this.im._connectionStats.totalMessagesReceived++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 更新消息发送统计
|
|
75
|
+
*/
|
|
76
|
+
}, {
|
|
77
|
+
key: "updateMessageSentStats",
|
|
78
|
+
value: function updateMessageSentStats() {
|
|
79
|
+
this.im._connectionStats.totalMessagesSent++;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 更新连接关闭统计
|
|
84
|
+
*/
|
|
85
|
+
}, {
|
|
86
|
+
key: "updateConnectionCloseStats",
|
|
87
|
+
value: function updateConnectionCloseStats() {
|
|
88
|
+
// 更新断开连接统计信息
|
|
89
|
+
if (this.im._connectionStats.lastConnectTime > 0) {
|
|
90
|
+
var now = Date.now();
|
|
91
|
+
this.im._connectionStats.lastDisconnectTime = now;
|
|
92
|
+
this.im._connectionStats.totalOnlineTime += now - this.im._connectionStats.lastConnectTime;
|
|
93
|
+
this.im._connectionStats.currentOnlineTime = 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 获取连接统计信息(用于监控)
|
|
99
|
+
* @returns {object} 连接统计信息
|
|
100
|
+
*/
|
|
101
|
+
}, {
|
|
102
|
+
key: "getConnectionStats",
|
|
103
|
+
value: function getConnectionStats() {
|
|
104
|
+
// 更新当前在线时长
|
|
105
|
+
if (this.im._connectionStats.lastConnectTime > 0) {
|
|
106
|
+
this.im._connectionStats.currentOnlineTime = Date.now() - this.im._connectionStats.lastConnectTime;
|
|
107
|
+
}
|
|
108
|
+
return _objectSpread({}, this.im._connectionStats);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 获取性能指标(用于监控)
|
|
113
|
+
* @returns {object} 性能指标
|
|
114
|
+
*/
|
|
115
|
+
}, {
|
|
116
|
+
key: "getPerformanceMetrics",
|
|
117
|
+
value: function getPerformanceMetrics() {
|
|
118
|
+
var stats = this.getConnectionStats();
|
|
119
|
+
|
|
120
|
+
// 连接成功率 = (总连接次数 - 总心跳超时次数) / 总连接次数
|
|
121
|
+
var connectionSuccessRate = stats.totalConnections > 0 ? (stats.totalConnections - stats.totalHeartbeatTimeouts) / stats.totalConnections : 1;
|
|
122
|
+
|
|
123
|
+
// 消息丢失率 = (发送消息数 - 接收ACK数) / 发送消息数
|
|
124
|
+
// 注意:这里简化处理,实际应该跟踪ACK确认数
|
|
125
|
+
var messageLossRate = stats.totalMessagesSent > 0 ? (stats.totalMessagesSent - stats.totalMessagesReceived) / stats.totalMessagesSent : 0;
|
|
126
|
+
|
|
127
|
+
// 平均重连时间(简化处理)
|
|
128
|
+
var averageReconnectionTime = stats.totalReconnections > 0 ? stats.totalOnlineTime / stats.totalReconnections : 0;
|
|
129
|
+
|
|
130
|
+
// 心跳超时率 = 心跳超时次数 / 总连接次数
|
|
131
|
+
var heartbeatTimeoutRate = stats.totalConnections > 0 ? stats.totalHeartbeatTimeouts / stats.totalConnections : 0;
|
|
132
|
+
return {
|
|
133
|
+
connectionSuccessRate: connectionSuccessRate,
|
|
134
|
+
messageLossRate: messageLossRate,
|
|
135
|
+
averageReconnectionTime: averageReconnectionTime,
|
|
136
|
+
heartbeatTimeoutRate: heartbeatTimeoutRate
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 重置连接统计信息
|
|
142
|
+
*/
|
|
143
|
+
}, {
|
|
144
|
+
key: "resetConnectionStats",
|
|
145
|
+
value: function resetConnectionStats() {
|
|
146
|
+
this.im._connectionStats = {
|
|
147
|
+
totalConnections: 0,
|
|
148
|
+
totalReconnections: 0,
|
|
149
|
+
totalMessagesSent: 0,
|
|
150
|
+
totalMessagesReceived: 0,
|
|
151
|
+
totalHeartbeatTimeouts: 0,
|
|
152
|
+
firstConnectionTime: 0,
|
|
153
|
+
lastConnectTime: 0,
|
|
154
|
+
lastDisconnectTime: 0,
|
|
155
|
+
totalOnlineTime: 0,
|
|
156
|
+
currentOnlineTime: 0
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}]);
|
|
160
|
+
return MonitoringManager;
|
|
161
|
+
}();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RECONNECT_RESON } from './common';
|
|
2
|
+
/**
|
|
3
|
+
* 重连状态枚举
|
|
4
|
+
* 使用状态机模式管理重连流程,避免 boolean 标记的不确定性
|
|
5
|
+
*/
|
|
6
|
+
export declare enum ReconnectState {
|
|
7
|
+
IDLE = "IDLE",// 空闲状态,没有重连
|
|
8
|
+
SCHEDULED = "SCHEDULED",// 已调度,等待定时器触发
|
|
9
|
+
CONNECTING = "CONNECTING"
|
|
10
|
+
}
|
|
11
|
+
export interface IReconnect {
|
|
12
|
+
success: () => void;
|
|
13
|
+
fail: (code: RECONNECT_RESON, err?: string | Event) => void;
|
|
14
|
+
reset: () => void;
|
|
15
|
+
getState?: () => ReconnectState;
|
|
16
|
+
getCounter?: () => number;
|
|
17
|
+
getMaxRetry?: () => number;
|
|
18
|
+
}
|
|
19
|
+
export default function (im: any): IReconnect;
|
package/es/reconnect.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
3
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
4
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
5
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
7
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
8
|
+
/* eslint-disable no-underscore-dangle */
|
|
9
|
+
import { EXCEPTION_ERROR } from "./common";
|
|
10
|
+
import { logger } from "./logger";
|
|
11
|
+
import { isMobile } from "./util";
|
|
12
|
+
/**
|
|
13
|
+
* 重连状态枚举
|
|
14
|
+
* 使用状态机模式管理重连流程,避免 boolean 标记的不确定性
|
|
15
|
+
*/
|
|
16
|
+
export var ReconnectState = /*#__PURE__*/function (ReconnectState) {
|
|
17
|
+
ReconnectState["IDLE"] = "IDLE";
|
|
18
|
+
ReconnectState["SCHEDULED"] = "SCHEDULED";
|
|
19
|
+
ReconnectState["CONNECTING"] = "CONNECTING";
|
|
20
|
+
return ReconnectState;
|
|
21
|
+
}({}); // 正在连接中
|
|
22
|
+
export default function (im) {
|
|
23
|
+
/**
|
|
24
|
+
* 重连最大次数默认值: 10 次 (约 3 分钟)
|
|
25
|
+
*
|
|
26
|
+
* 时间计算 (指数退避 + 随机抖动):
|
|
27
|
+
* - 10 次: ~181s ≈ 3 分钟
|
|
28
|
+
* - 15 次: ~331s ≈ 5.5 分钟
|
|
29
|
+
* - 20 次: ~481s ≈ 8 分钟
|
|
30
|
+
*
|
|
31
|
+
* 重连延迟序列: 1s → 2s → 4s → 8s → 16s → 30s(最大) × N
|
|
32
|
+
*/
|
|
33
|
+
var _im$options$reconnect = im.options.reconnectMaxRetry,
|
|
34
|
+
reconnectMaxRetry = _im$options$reconnect === void 0 ? 10 : _im$options$reconnect;
|
|
35
|
+
var Reconnect = /*#__PURE__*/function () {
|
|
36
|
+
function Reconnect() {
|
|
37
|
+
_classCallCheck(this, Reconnect);
|
|
38
|
+
_defineProperty(this, "_state", void 0);
|
|
39
|
+
_defineProperty(this, "_counter", void 0);
|
|
40
|
+
_defineProperty(this, "_maxRetry", void 0);
|
|
41
|
+
_defineProperty(this, "_reconnectTimer", void 0);
|
|
42
|
+
this._state = ReconnectState.IDLE;
|
|
43
|
+
this._counter = 0;
|
|
44
|
+
this._maxRetry = reconnectMaxRetry;
|
|
45
|
+
this._reconnectTimer = null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 连接异常后需要用户手动操作
|
|
50
|
+
* 否则可能引起死循环
|
|
51
|
+
*/
|
|
52
|
+
_createClass(Reconnect, [{
|
|
53
|
+
key: "_exception",
|
|
54
|
+
value: function _exception(code, err) {
|
|
55
|
+
// 清理定时器并重置状态
|
|
56
|
+
if (this._reconnectTimer) {
|
|
57
|
+
clearTimeout(this._reconnectTimer);
|
|
58
|
+
this._reconnectTimer = null;
|
|
59
|
+
}
|
|
60
|
+
this._state = ReconnectState.IDLE;
|
|
61
|
+
var msg = {
|
|
62
|
+
code: code,
|
|
63
|
+
err: err
|
|
64
|
+
};
|
|
65
|
+
im.closeEvent(msg);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 标记重连已调度(定时器已设置)
|
|
70
|
+
*/
|
|
71
|
+
}, {
|
|
72
|
+
key: "_scheduleReconnect",
|
|
73
|
+
value: function _scheduleReconnect() {
|
|
74
|
+
this._state = ReconnectState.SCHEDULED;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 标记正在连接中
|
|
79
|
+
*/
|
|
80
|
+
}, {
|
|
81
|
+
key: "_startConnecting",
|
|
82
|
+
value: function _startConnecting() {
|
|
83
|
+
this._state = ReconnectState.CONNECTING;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 重连成功,重置所有状态
|
|
88
|
+
*/
|
|
89
|
+
}, {
|
|
90
|
+
key: "_end",
|
|
91
|
+
value: function _end() {
|
|
92
|
+
this._counter = 0;
|
|
93
|
+
this._state = ReconnectState.IDLE;
|
|
94
|
+
if (this._reconnectTimer) {
|
|
95
|
+
clearTimeout(this._reconnectTimer);
|
|
96
|
+
this._reconnectTimer = null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 指数退避算法 + 随机抖动
|
|
102
|
+
* 避免大量客户端同时重连造成惊群效应
|
|
103
|
+
* 移动端使用更积极的重连策略
|
|
104
|
+
*/
|
|
105
|
+
}, {
|
|
106
|
+
key: "_backoff",
|
|
107
|
+
value: function _backoff(attempt) {
|
|
108
|
+
// 基础延迟 1 秒,最大延迟 30 秒
|
|
109
|
+
var baseDelay = isMobile() ? 500 : 1000; // 移动端更短的基础延迟
|
|
110
|
+
var maxDelay = 30000;
|
|
111
|
+
|
|
112
|
+
// 指数退避:1s, 2s, 4s, 8s, 16s, 30s, 30s...
|
|
113
|
+
var exponentialDelay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
|
|
114
|
+
|
|
115
|
+
// 添加 ±30% 的随机抖动
|
|
116
|
+
var jitter = exponentialDelay * 0.3 * (Math.random() - 0.5);
|
|
117
|
+
return Math.floor(exponentialDelay + jitter);
|
|
118
|
+
}
|
|
119
|
+
}, {
|
|
120
|
+
key: "_exec",
|
|
121
|
+
value: function _exec(code, err) {
|
|
122
|
+
var _this = this;
|
|
123
|
+
// 防止重复调度重连
|
|
124
|
+
if (this._state !== ReconnectState.IDLE) {
|
|
125
|
+
logger.debug("[Reconnect] Reconnect already in progress (state: ".concat(this._state, "), skipping..."));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 取消之前的重连计划(防御性编程)
|
|
130
|
+
if (this._reconnectTimer) {
|
|
131
|
+
clearTimeout(this._reconnectTimer);
|
|
132
|
+
this._reconnectTimer = null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 使用指数退避算法确定重连时间,防止并发过多
|
|
136
|
+
var t = this._backoff(this._counter);
|
|
137
|
+
logger.debug("[Reconnect] Scheduling reconnect in ".concat(t, "ms (attempt ").concat(this._counter + 1, "/").concat(this._maxRetry, ")"), {
|
|
138
|
+
code: code
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// 标记为已调度状态
|
|
142
|
+
this._scheduleReconnect();
|
|
143
|
+
|
|
144
|
+
// 倒计时后开始重连
|
|
145
|
+
this._reconnectTimer = window.setTimeout(function () {
|
|
146
|
+
_this._reconnectTimer = null;
|
|
147
|
+
|
|
148
|
+
// 标记为正在连接状态
|
|
149
|
+
_this._startConnecting();
|
|
150
|
+
try {
|
|
151
|
+
// 主动关闭 IM 连接
|
|
152
|
+
im.close();
|
|
153
|
+
// 重新建连
|
|
154
|
+
im.connect();
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// 连接失败,触发异常处理
|
|
157
|
+
_this._exception(EXCEPTION_ERROR.RECONNECT_ERROR, {
|
|
158
|
+
code: EXCEPTION_ERROR.RECONNECT_ERROR,
|
|
159
|
+
err: {
|
|
160
|
+
code: code,
|
|
161
|
+
err: err,
|
|
162
|
+
error: error
|
|
163
|
+
},
|
|
164
|
+
msg: '重连失败',
|
|
165
|
+
context: {
|
|
166
|
+
attempt: _this._counter,
|
|
167
|
+
maxRetry: _this._maxRetry,
|
|
168
|
+
reconnectReason: code
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 发送重连事件
|
|
175
|
+
im.reconnectEvent({
|
|
176
|
+
code: code,
|
|
177
|
+
err: err,
|
|
178
|
+
attempt: _this._counter + 1,
|
|
179
|
+
maxRetries: _this._maxRetry,
|
|
180
|
+
nextRetryDelay: _this._backoff(_this._counter + 1)
|
|
181
|
+
});
|
|
182
|
+
}, t);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 连接成功清除相关标记
|
|
187
|
+
*/
|
|
188
|
+
}, {
|
|
189
|
+
key: "success",
|
|
190
|
+
value: function success() {
|
|
191
|
+
this._end();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 连接失败后自动重连
|
|
196
|
+
* @param {String} code - 重连原因
|
|
197
|
+
* @param {String | Event} err - 错误信息
|
|
198
|
+
*/
|
|
199
|
+
}, {
|
|
200
|
+
key: "fail",
|
|
201
|
+
value: function fail(code, err) {
|
|
202
|
+
// 如果正在连接中,将状态重置为空闲以允许重新调度
|
|
203
|
+
if (this._state === ReconnectState.CONNECTING) {
|
|
204
|
+
this._state = ReconnectState.IDLE;
|
|
205
|
+
}
|
|
206
|
+
// 如果已经调度,需要先清理定时器
|
|
207
|
+
else if (this._state === ReconnectState.SCHEDULED && this._reconnectTimer) {
|
|
208
|
+
clearTimeout(this._reconnectTimer);
|
|
209
|
+
this._reconnectTimer = null;
|
|
210
|
+
this._state = ReconnectState.IDLE;
|
|
211
|
+
}
|
|
212
|
+
this._counter = this._counter + 1;
|
|
213
|
+
|
|
214
|
+
// 自动重连最多 this._maxRetry 次,否则需要用户手动重连
|
|
215
|
+
if (this._counter > this._maxRetry) {
|
|
216
|
+
// 这里不能直接重置计数器为0
|
|
217
|
+
// 而是在UI上做体现,提醒用户手动重连
|
|
218
|
+
// 不然会死循环
|
|
219
|
+
return this._exception(EXCEPTION_ERROR.NEED_RECONNECT_MANUAL, {
|
|
220
|
+
code: EXCEPTION_ERROR.NEED_RECONNECT_MANUAL,
|
|
221
|
+
err: {
|
|
222
|
+
reconnectReason: code,
|
|
223
|
+
reconnectError: err
|
|
224
|
+
},
|
|
225
|
+
msg: '重连次数超过最大值',
|
|
226
|
+
maxRetry: this._maxRetry
|
|
227
|
+
});
|
|
228
|
+
} else {
|
|
229
|
+
// 执行自动重连
|
|
230
|
+
this._exec(code, err);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* 重置重连状态(供手动重连使用)
|
|
236
|
+
*/
|
|
237
|
+
}, {
|
|
238
|
+
key: "reset",
|
|
239
|
+
value: function reset() {
|
|
240
|
+
this._counter = 0;
|
|
241
|
+
this._state = ReconnectState.IDLE;
|
|
242
|
+
if (this._reconnectTimer) {
|
|
243
|
+
clearTimeout(this._reconnectTimer);
|
|
244
|
+
this._reconnectTimer = null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 获取当前重连状态(用于调试和监控)
|
|
250
|
+
*/
|
|
251
|
+
}, {
|
|
252
|
+
key: "getState",
|
|
253
|
+
value: function getState() {
|
|
254
|
+
return this._state;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 获取当前重连次数(用于调试和监控)
|
|
259
|
+
*/
|
|
260
|
+
}, {
|
|
261
|
+
key: "getCounter",
|
|
262
|
+
value: function getCounter() {
|
|
263
|
+
return this._counter;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 获取最大重连次数(用于调试和监控)
|
|
268
|
+
*/
|
|
269
|
+
}, {
|
|
270
|
+
key: "getMaxRetry",
|
|
271
|
+
value: function getMaxRetry() {
|
|
272
|
+
return this._maxRetry;
|
|
273
|
+
}
|
|
274
|
+
}]);
|
|
275
|
+
return Reconnect;
|
|
276
|
+
}();
|
|
277
|
+
var r = new Reconnect();
|
|
278
|
+
return r;
|
|
279
|
+
}
|
package/es/socket.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { WS_READYSTATE } from './common';
|
|
2
|
+
import { WS_STATUS } from './common';
|
|
3
|
+
interface Config {
|
|
4
|
+
url: string;
|
|
5
|
+
token: string;
|
|
6
|
+
appKey: string;
|
|
7
|
+
}
|
|
8
|
+
export default class Socket {
|
|
9
|
+
ws: WebSocket;
|
|
10
|
+
config: Config;
|
|
11
|
+
emit: (type: string, data?: any) => void;
|
|
12
|
+
on: (type: string, handler: (data: any) => void) => void;
|
|
13
|
+
private _eventHandlers;
|
|
14
|
+
constructor(config: Config);
|
|
15
|
+
/**
|
|
16
|
+
* 获取 WebSocket 的 readyState
|
|
17
|
+
*/
|
|
18
|
+
get readyState(): WS_READYSTATE;
|
|
19
|
+
/**
|
|
20
|
+
* 自动根据 WebSocket 的实际状态计算 status
|
|
21
|
+
* 使用 getter 避免状态不同步问题
|
|
22
|
+
*/
|
|
23
|
+
get status(): WS_STATUS;
|
|
24
|
+
send(data: string): void;
|
|
25
|
+
close(): void;
|
|
26
|
+
private _bindEvts;
|
|
27
|
+
private _unbindEvts;
|
|
28
|
+
}
|
|
29
|
+
export {};
|