浏览代码

feat: Reduce pings and adds xmpp ping config (#1389)

* feat: Skips pinging when there was a recent server response.

* feat: Adds options to control xmpp ping settings.

* fix: Fixes wrong default value.

* squash: Received messages in ping interval sets failed ping to 0
dev1
Дамян Минков 4 年前
父节点
当前提交
6bb0b86c0a
没有帐户链接到提交者的电子邮件
共有 4 个文件被更改,包括 76 次插入26 次删除
  1. 4
    0
      doc/API.md
  2. 5
    2
      modules/xmpp/XmppConnection.js
  3. 60
    21
      modules/xmpp/strophe.ping.js
  4. 7
    3
      modules/xmpp/xmpp.js

+ 4
- 0
doc/API.md 查看文件

@@ -227,6 +227,10 @@ This objects represents the server connection. You can create new `JitsiConnecti
227 227
             - `anonymousdomain`
228 228
         4. `enableLipSync` - (optional) boolean property which enables the lipsync feature. Currently works only in Chrome and is disabled by default.
229 229
         5. `clientNode` - The name of client node advertised in XEP-0115 'c' stanza
230
+        6. xmppPing - (optional) JS Object - xmpp ping options
231
+            - `interval` - how often to send ping requests, default: 10000 (10 seconds)
232
+            - `timeout` - the time to wait for ping responses, default: 5000 (5 seconds)
233
+            - `threshold` - how many ping failures will be tolerated before the connection is killed, default: 2
230 234
 
231 235
 2. `connect(options)` - establish server connection
232 236
     - `options` - JS Object with `id` and `password` properties.

+ 5
- 2
modules/xmpp/XmppConnection.js 查看文件

@@ -45,8 +45,9 @@ export default class XmppConnection extends Listenable {
45 45
      * default with jitter. Pass -1 to disable. The actual interval equation is:
46 46
      * jitterDelay = (interval * 0.2) + (0.8 * interval * Math.random())
47 47
      * The keep alive is HTTP GET request to the {@link options.serviceUrl}.
48
+     * @param {Object} [options.xmppPing] - The xmpp ping settings.
48 49
      */
49
-    constructor({ enableWebsocketResume, websocketKeepAlive, serviceUrl }) {
50
+    constructor({ enableWebsocketResume, websocketKeepAlive, serviceUrl, xmppPing }) {
50 51
         super();
51 52
         this._options = {
52 53
             enableWebsocketResume: typeof enableWebsocketResume === 'undefined' ? true : enableWebsocketResume,
@@ -83,7 +84,9 @@ export default class XmppConnection extends Listenable {
83 84
         this.addConnectionPlugin(
84 85
             'ping',
85 86
             new PingConnectionPlugin({
86
-                onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded()
87
+                getTimeSinceLastServerResponse: () => this.getTimeSinceLastSuccess(),
88
+                onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded(),
89
+                pingOptions: xmppPing
87 90
             }));
88 91
     }
89 92
 

+ 60
- 21
modules/xmpp/strophe.ping.js 查看文件

@@ -9,28 +9,36 @@ import ConnectionPlugin from './ConnectionPlugin';
9 9
 const logger = getLogger(__filename);
10 10
 
11 11
 /**
12
- * Ping every 10 sec
12
+ * Default ping every 10 sec
13 13
  */
14
-const PING_INTERVAL = 10000;
14
+const PING_DEFAULT_INTERVAL = 10000;
15 15
 
16 16
 /**
17
- * Ping timeout error after 5 sec of waiting.
17
+ * Default ping timeout error after 5 sec of waiting.
18 18
  */
19
-const PING_TIMEOUT = 5000;
19
+const PING_DEFAULT_TIMEOUT = 5000;
20 20
 
21 21
 /**
22
- * How many ping failures will be tolerated before the WebSocket connection is killed.
22
+ * Default value for how many ping failures will be tolerated before the WebSocket connection is killed.
23 23
  * The worst case scenario in case of ping timing out without a response is (25 seconds at the time of this writing):
24 24
  * PING_THRESHOLD * PING_INTERVAL + PING_TIMEOUT
25 25
  */
26
-const PING_THRESHOLD = 2;
26
+const PING_DEFAULT_THRESHOLD = 2;
27 27
 
28 28
 /**
29
- * The number of timestamps of send pings to keep.
30
- * The current value is 2 minutes.
31
- * @type {number} number of timestamps.
29
+ * How often to send ping requests.
32 30
  */
33
-const PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;
31
+let pingInterval;
32
+
33
+/**
34
+ * The time to wait for ping responses.
35
+ */
36
+let pingTimeout;
37
+
38
+/**
39
+ * How many ping failures will be tolerated before the connection is killed.
40
+ */
41
+let pingThreshold;
34 42
 
35 43
 /**
36 44
  * XEP-0199 ping plugin.
@@ -39,17 +47,30 @@ const PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;
39 47
  */
40 48
 export default class PingConnectionPlugin extends ConnectionPlugin {
41 49
     /**
42
-     * Contructs new object
50
+     * Constructs new object
43 51
      * @param {Object} options
44 52
      * @param {Function} options.onPingThresholdExceeded - Callback called when ping fails too many times (controlled
45 53
      * by the {@link PING_THRESHOLD} constant).
54
+     * @param {Function} options._getTimeSinceLastServerResponse - A function to obtain the last seen
55
+     * response from the server.
56
+     * @param {Object} options.pingOptions - The ping options if any.
46 57
      * @constructor
47 58
      */
48
-    constructor({ onPingThresholdExceeded }) {
59
+    constructor({ getTimeSinceLastServerResponse, onPingThresholdExceeded, pingOptions = {} }) {
49 60
         super();
50 61
         this.failedPings = 0;
51
-        this.pingExecIntervals = new Array(PING_TIMESTAMPS_TO_KEEP);
52 62
         this._onPingThresholdExceeded = onPingThresholdExceeded;
63
+        this._getTimeSinceLastServerResponse = getTimeSinceLastServerResponse;
64
+
65
+        this.pingInterval = typeof pingOptions.interval === 'number' ? pingOptions.interval : PING_DEFAULT_INTERVAL;
66
+        this.pingTimeout = typeof pingOptions.timeout === 'number' ? pingOptions.timeout : PING_DEFAULT_TIMEOUT;
67
+        this.pingThreshold = typeof pingOptions.threshold === 'number'
68
+            ? pingOptions.threshold : PING_DEFAULT_THRESHOLD;
69
+
70
+        // The number of timestamps of send pings to keep.
71
+        // The current value is 2 minutes.
72
+        this.pingTimestampsToKeep = Math.round(120000 / this.pingInterval);
73
+        this.pingExecIntervals = new Array(this.pingTimestampsToKeep);
53 74
     }
54 75
 
55 76
     /**
@@ -91,27 +112,45 @@ export default class PingConnectionPlugin extends ConnectionPlugin {
91 112
      * This plugin supports only one such task and <tt>stopInterval</tt>
92 113
      * must be called before starting a new one.
93 114
      * @param remoteJid remote JID to which ping requests will be sent to.
94
-     * @param interval task interval in ms.
95 115
      */
96
-    startInterval(remoteJid, interval = PING_INTERVAL) {
116
+    startInterval(remoteJid) {
97 117
         clearInterval(this.intervalId);
98 118
         this.intervalId = window.setInterval(() => {
119
+
120
+            // when there were some server responses in the interval since the last time we checked (_lastServerCheck)
121
+            // let's skip the ping
122
+
123
+            // server response is measured on raw input and ping response time is measured after all the xmpp
124
+            // processing is done, and when the last server response is a ping there can be slight misalignment of the
125
+            // times, we give it 100ms for that processing.
126
+            if (this._getTimeSinceLastServerResponse() + 100 < new Date() - this._lastServerCheck) {
127
+                // do this just to keep in sync the intervals so we can detect suspended device
128
+                this._addPingExecutionTimestamp();
129
+
130
+                this._lastServerCheck = new Date();
131
+                this.failedPings = 0;
132
+
133
+                return;
134
+            }
135
+
99 136
             this.ping(remoteJid, () => {
137
+                this._lastServerCheck = new Date();
138
+
100 139
                 this.failedPings = 0;
101 140
             }, error => {
102 141
                 this.failedPings += 1;
103 142
                 const errmsg = `Ping ${error ? 'error' : 'timeout'}`;
104 143
 
105
-                if (this.failedPings >= PING_THRESHOLD) {
144
+                if (this.failedPings >= pingThreshold) {
106 145
                     GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));
107 146
                     logger.error(errmsg, error);
108 147
                     this._onPingThresholdExceeded && this._onPingThresholdExceeded();
109 148
                 } else {
110 149
                     logger.warn(errmsg, error);
111 150
                 }
112
-            }, PING_TIMEOUT);
113
-        }, interval);
114
-        logger.info(`XMPP pings will be sent every ${interval} ms`);
151
+            }, pingTimeout);
152
+        }, this.pingInterval);
153
+        logger.info(`XMPP pings will be sent every ${this.pingInterval} ms`);
115 154
     }
116 155
 
117 156
     /**
@@ -134,7 +173,7 @@ export default class PingConnectionPlugin extends ConnectionPlugin {
134 173
         this.pingExecIntervals.push(new Date().getTime());
135 174
 
136 175
         // keep array length to PING_TIMESTAMPS_TO_KEEP
137
-        if (this.pingExecIntervals.length > PING_TIMESTAMPS_TO_KEEP) {
176
+        if (this.pingExecIntervals.length > this.pingTimestampsToKeep) {
138 177
             this.pingExecIntervals.shift();
139 178
         }
140 179
     }
@@ -172,7 +211,7 @@ export default class PingConnectionPlugin extends ConnectionPlugin {
172 211
         // remove the interval between the ping sent
173 212
         // this way in normal execution there is no suspend and the return
174 213
         // will be 0 or close to 0.
175
-        maxInterval -= PING_INTERVAL;
214
+        maxInterval -= pingInterval;
176 215
 
177 216
         // make sure we do not return less than 0
178 217
         return Math.max(maxInterval, 0);

+ 7
- 3
modules/xmpp/xmpp.js 查看文件

@@ -32,9 +32,10 @@ const logger = getLogger(__filename);
32 32
  * @param {string} options.serviceUrl - The service URL for XMPP connection.
33 33
  * @param {string} options.enableWebsocketResume - True to enable stream resumption.
34 34
  * @param {number} [options.websocketKeepAlive] - See {@link XmppConnection} constructor.
35
+ * @param {Object} [options.xmppPing] - See {@link XmppConnection} constructor.
35 36
  * @returns {XmppConnection}
36 37
  */
37
-function createConnection({ enableWebsocketResume, serviceUrl = '/http-bind', token, websocketKeepAlive }) {
38
+function createConnection({ enableWebsocketResume, serviceUrl = '/http-bind', token, websocketKeepAlive, xmppPing }) {
38 39
     // Append token as URL param
39 40
     if (token) {
40 41
         // eslint-disable-next-line no-param-reassign
@@ -44,7 +45,8 @@ function createConnection({ enableWebsocketResume, serviceUrl = '/http-bind', to
44 45
     return new XmppConnection({
45 46
         enableWebsocketResume,
46 47
         serviceUrl,
47
-        websocketKeepAlive
48
+        websocketKeepAlive,
49
+        xmppPing
48 50
     });
49 51
 }
50 52
 
@@ -91,6 +93,7 @@ export default class XMPP extends Listenable {
91 93
      * module try to resume the session in case the Websocket connection breaks.
92 94
      * @param {number} [options.websocketKeepAlive] - The websocket keep alive interval. See {@link XmppConnection}
93 95
      * constructor for more details.
96
+     * @param {Object} [options.xmppPing] - The xmpp ping settings.
94 97
      * @param {Array<Object>} options.p2pStunServers see {@link JingleConnectionPlugin} for more details.
95 98
      * @param token
96 99
      */
@@ -111,7 +114,8 @@ export default class XMPP extends Listenable {
111 114
             // FIXME remove deprecated bosh option at some point
112 115
             serviceUrl: options.serviceUrl || options.bosh,
113 116
             token,
114
-            websocketKeepAlive: options.websocketKeepAlive
117
+            websocketKeepAlive: options.websocketKeepAlive,
118
+            xmppPing: options.xmppPing
115 119
         });
116 120
 
117 121
         this._initStrophePlugins();

正在加载...
取消
保存