amqp-connection-manager 2.2.0 → 2.3.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.
@@ -1,207 +1,203 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
3
  Object.defineProperty(exports, "__esModule", {
4
- value: true
4
+ value: true
5
5
  });
6
+ exports.default = void 0;
6
7
 
7
- var _events = require('events');
8
+ var _events = require("events");
8
9
 
9
- var _amqplib = require('amqplib');
10
+ var _amqplib = _interopRequireDefault(require("amqplib"));
10
11
 
11
- var _amqplib2 = _interopRequireDefault(_amqplib);
12
+ var _url = _interopRequireDefault(require("url"));
12
13
 
13
- var _url = require('url');
14
+ var _ChannelWrapper = _interopRequireDefault(require("./ChannelWrapper"));
14
15
 
15
- var _url2 = _interopRequireDefault(_url);
16
+ var _helpers = require("./helpers");
16
17
 
17
- var _ChannelWrapper = require('./ChannelWrapper');
18
-
19
- var _ChannelWrapper2 = _interopRequireDefault(_ChannelWrapper);
20
-
21
- var _helpers = require('./helpers');
22
-
23
- var _promiseBreaker = require('promise-breaker');
24
-
25
- var _promiseBreaker2 = _interopRequireDefault(_promiseBreaker);
18
+ var _promiseBreaker = _interopRequireDefault(require("promise-breaker"));
26
19
 
27
20
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
21
 
29
22
  // Default heartbeat time.
30
23
  const HEARTBEAT_IN_SECONDS = 5;
31
-
32
24
  /* istanbul ignore next */
33
- function neverThrows() {
34
- return err => setImmediate(() => {
35
- throw new Error(`AmqpConnectionManager - should never get here: ${err.message}\n` + err.stack);
36
- });
37
- }
38
25
 
39
- //
26
+ function neverThrows() {
27
+ return err => setImmediate(() => {
28
+ throw new Error(`AmqpConnectionManager - should never get here: ${err.message}\n` + err.stack);
29
+ });
30
+ } //
40
31
  // Events:
41
32
  // * `connect({connection, url})` - Emitted whenever we connect to a broker.
42
33
  // * `disconnect({err})` - Emitted whenever we disconnect from a broker.
43
34
  //
35
+
36
+
44
37
  class AmqpConnectionManager extends _events.EventEmitter {
45
- /**
46
- * Create a new AmqplibConnectionManager.
47
- *
48
- * @param {(string|Object)[]} urls - An array of brokers to connect to.
49
- * Takes url strings or objects {url: string, connectionOptions?: object}
50
- * If present, a broker's [connectionOptions] will be used instead
51
- * of [options.connectionOptions] when passed to the amqplib connect method.
52
- * AmqplibConnectionManager will round-robin between them whenever it
53
- * needs to create a new connection.
54
- * @param {Object} [options={}] -
55
- * @param {number} [options.heartbeatIntervalInSeconds=5] - The interval,
56
- * in seconds, to send heartbeats.
57
- * @param {number} [options.reconnectTimeInSeconds] - The time to wait
58
- * before trying to reconnect. If not specified, defaults to
59
- * `heartbeatIntervalInSeconds`.
60
- * @param {Object} [options.connectionOptions] - Passed to the amqplib
61
- * connect method.
62
- * @param {function} [options.findServers] - A `fn(callback)` or a `fn()`
63
- * which returns a Promise. This should resolve to one or more servers
64
- * to connect to, either a single URL or an array of URLs. This is handy
65
- * when you're using a service discovery mechanism such as Consul or etcd.
66
- * Note that if this is supplied, then `urls` is ignored.
67
- */
68
- constructor(urls, options = {}) {
69
- super();
70
- if (!urls && !options.findServers) {
71
- throw new Error("Must supply either `urls` or `findServers`");
72
- }
73
- this._channels = [];
38
+ /**
39
+ * Create a new AmqplibConnectionManager.
40
+ *
41
+ * @param {(string|Object)[]} urls - An array of brokers to connect to.
42
+ * Takes url strings or objects {url: string, connectionOptions?: object}
43
+ * If present, a broker's [connectionOptions] will be used instead
44
+ * of [options.connectionOptions] when passed to the amqplib connect method.
45
+ * AmqplibConnectionManager will round-robin between them whenever it
46
+ * needs to create a new connection.
47
+ * @param {Object} [options={}] -
48
+ * @param {number} [options.heartbeatIntervalInSeconds=5] - The interval,
49
+ * in seconds, to send heartbeats.
50
+ * @param {number} [options.reconnectTimeInSeconds] - The time to wait
51
+ * before trying to reconnect. If not specified, defaults to
52
+ * `heartbeatIntervalInSeconds`.
53
+ * @param {Object} [options.connectionOptions] - Passed to the amqplib
54
+ * connect method.
55
+ * @param {function} [options.findServers] - A `fn(callback)` or a `fn()`
56
+ * which returns a Promise. This should resolve to one or more servers
57
+ * to connect to, either a single URL or an array of URLs. This is handy
58
+ * when you're using a service discovery mechanism such as Consul or etcd.
59
+ * Note that if this is supplied, then `urls` is ignored.
60
+ */
61
+ constructor(urls, options = {}) {
62
+ super();
63
+
64
+ if (!urls && !options.findServers) {
65
+ throw new Error("Must supply either `urls` or `findServers`");
66
+ }
74
67
 
75
- this._currentUrl = 0;
76
- this.connectionOptions = options.connectionOptions;
68
+ this._channels = [];
69
+ this._currentUrl = 0;
70
+ this.connectionOptions = options.connectionOptions;
71
+ this.heartbeatIntervalInSeconds = options.heartbeatIntervalInSeconds || HEARTBEAT_IN_SECONDS;
72
+ this.reconnectTimeInSeconds = options.reconnectTimeInSeconds || this.heartbeatIntervalInSeconds; // There will be one listener per channel, and there could be a lot of channels, so disable warnings from node.
77
73
 
78
- this.heartbeatIntervalInSeconds = options.heartbeatIntervalInSeconds || HEARTBEAT_IN_SECONDS;
79
- this.reconnectTimeInSeconds = options.reconnectTimeInSeconds || this.heartbeatIntervalInSeconds;
74
+ this.setMaxListeners(0);
80
75
 
81
- // There will be one listener per channel, and there could be a lot of channels, so disable warnings from node.
82
- this.setMaxListeners(0);
76
+ this._findServers = options.findServers || (() => Promise.resolve(urls));
83
77
 
84
- this._findServers = options.findServers || (() => Promise.resolve(urls));
78
+ this._connect();
79
+ } // `options` here are any options that can be passed to ChannelWrapper.
85
80
 
86
- this._connect();
87
- }
88
81
 
89
- // `options` here are any options that can be passed to ChannelWrapper.
90
- createChannel(options = {}) {
91
- const channel = new _ChannelWrapper2.default(this, options);
92
- this._channels.push(channel);
93
- channel.once('close', () => {
94
- this._channels = this._channels.filter(c => c !== channel);
95
- });
96
- return channel;
97
- }
82
+ createChannel(options = {}) {
83
+ const channel = new _ChannelWrapper.default(this, options);
98
84
 
99
- close() {
100
- if (this._closed) {
101
- return Promise.resolve();
102
- }
103
- this._closed = true;
104
-
105
- return Promise.all(this._channels.map(channel => channel.close())).catch(function () {
106
- // Ignore errors closing channels.
107
- }).then(() => {
108
- this._channels = [];
109
- if (this._currentConnection) {
110
- this._currentConnection.removeAllListeners('close');
111
- this._currentConnection.close();
112
- }
113
- this._currentConnection = null;
114
- });
85
+ this._channels.push(channel);
86
+
87
+ channel.once('close', () => {
88
+ this._channels = this._channels.filter(c => c !== channel);
89
+ });
90
+ return channel;
91
+ }
92
+
93
+ close() {
94
+ if (this._closed) {
95
+ return Promise.resolve();
115
96
  }
116
97
 
117
- isConnected() {
118
- return !!this._currentConnection;
98
+ this._closed = true;
99
+ return Promise.all(this._channels.map(channel => channel.close())).catch(function () {// Ignore errors closing channels.
100
+ }).then(() => {
101
+ this._channels = [];
102
+
103
+ if (this._currentConnection) {
104
+ this._currentConnection.removeAllListeners('close');
105
+
106
+ this._currentConnection.close();
107
+ }
108
+
109
+ this._currentConnection = null;
110
+ });
111
+ }
112
+
113
+ isConnected() {
114
+ return !!this._currentConnection;
115
+ }
116
+
117
+ _connect() {
118
+ if (this._closed || this._connecting || this.isConnected()) {
119
+ return Promise.resolve();
119
120
  }
120
121
 
121
- _connect() {
122
- if (this._closed || this._connecting || this.isConnected()) {
123
- return Promise.resolve();
124
- }
125
-
126
- this._connecting = true;
127
-
128
- return Promise.resolve().then(() => {
129
- if (!this._urls || this._currentUrl >= this._urls.length) {
130
- this._currentUrl = 0;
131
- return _promiseBreaker2.default.callFn(this._findServers, 0, null);
132
- } else {
133
- return this._urls;
134
- }
135
- }).then(urls => {
136
- if (urls && !Array.isArray(urls)) {
137
- urls = [urls];
138
- }
139
- this._urls = urls;
140
-
141
- if (!urls || urls.length === 0) {
142
- throw new Error('amqp-connection-manager: No servers found');
143
- }
144
-
145
- // Round robin between brokers
146
- const url = urls[this._currentUrl];
147
- this._currentUrl++;
148
-
149
- // url can be a string or object {url: string, connectionOptions?: object}
150
- const urlString = url.url || url;
151
- const connectionOptions = url.connectionOptions || this.connectionOptions;
152
-
153
- const amqpUrl = _url2.default.parse(urlString);
154
- if (amqpUrl.search) {
155
- amqpUrl.search += `&heartbeat=${this.heartbeatIntervalInSeconds}`;
156
- } else {
157
- amqpUrl.search = `?heartbeat=${this.heartbeatIntervalInSeconds}`;
158
- }
159
-
160
- return _amqplib2.default.connect(_url2.default.format(amqpUrl), connectionOptions).then(connection => {
161
- this._currentConnection = connection;
162
-
163
- //emit 'blocked' when RabbitMQ server decides to block the connection (resources running low)
164
- connection.on('blocked', reason => this.emit('blocked', { reason }));
165
-
166
- connection.on('unblocked', () => this.emit('unblocked'));
167
-
168
- // Reconnect if the broker goes away.
169
- connection.on('error', err => Promise.resolve().then(() => this._currentConnection.close()).catch(() => {/* Ignore */}).then(() => {
170
- this._currentConnection = null;
171
- this.emit('disconnect', { err });
172
- return this._connect();
173
- })
174
- // `_connect()` should never throw.
175
- .catch(neverThrows));
176
-
177
- // Reconnect if the connection closes gracefully
178
- connection.on('close', err => {
179
- this._currentConnection = null;
180
- this.emit('disconnect', { err });
181
-
182
- (0, _helpers.wait)(this.reconnectTimeInSeconds * 1000).then(() => this._connect())
183
- // `_connect()` should never throw.
184
- .catch(neverThrows);
185
- });
186
-
187
- this._connecting = false;
188
- this.emit('connect', { connection, url: urlString });
189
-
190
- return null;
191
- });
192
- }).catch(err => {
193
- this.emit('disconnect', { err });
194
-
195
- // Connection failed...
196
- this._currentConnection = null;
197
-
198
- // TODO: Probably want to try right away here, especially if there are multiple brokers to try...
199
- return (0, _helpers.wait)(this.reconnectTimeInSeconds * 1000).then(() => {
200
- this._connecting = false;
201
- return this._connect();
202
- });
122
+ this._connecting = true;
123
+ return Promise.resolve().then(() => {
124
+ if (!this._urls || this._currentUrl >= this._urls.length) {
125
+ this._currentUrl = 0;
126
+ return _promiseBreaker.default.callFn(this._findServers, 0, null);
127
+ } else {
128
+ return this._urls;
129
+ }
130
+ }).then(urls => {
131
+ if (urls && !Array.isArray(urls)) {
132
+ urls = [urls];
133
+ }
134
+
135
+ this._urls = urls;
136
+
137
+ if (!urls || urls.length === 0) {
138
+ throw new Error('amqp-connection-manager: No servers found');
139
+ } // Round robin between brokers
140
+
141
+
142
+ const url = urls[this._currentUrl];
143
+ this._currentUrl++; // url can be a string or object {url: string, connectionOptions?: object}
144
+
145
+ const urlString = url.url || url;
146
+ const connectionOptions = url.connectionOptions || this.connectionOptions;
147
+
148
+ const amqpUrl = _url.default.parse(urlString);
149
+
150
+ if (amqpUrl.search) {
151
+ amqpUrl.search += `&heartbeat=${this.heartbeatIntervalInSeconds}`;
152
+ } else {
153
+ amqpUrl.search = `?heartbeat=${this.heartbeatIntervalInSeconds}`;
154
+ }
155
+
156
+ return _amqplib.default.connect(_url.default.format(amqpUrl), connectionOptions).then(connection => {
157
+ this._currentConnection = connection; //emit 'blocked' when RabbitMQ server decides to block the connection (resources running low)
158
+
159
+ connection.on('blocked', reason => this.emit('blocked', {
160
+ reason
161
+ }));
162
+ connection.on('unblocked', () => this.emit('unblocked'));
163
+ connection.on('error', () =>
164
+ /* err */
165
+ {// if this event was emitted, then the connection was already closed,
166
+ // so no need to call #close here
167
+ // also, 'close' is emitted after 'error',
168
+ // so no need for work already done in 'close' handler
169
+ }); // Reconnect if the connection closes
170
+
171
+ connection.on('close', err => {
172
+ this._currentConnection = null;
173
+ this.emit('disconnect', {
174
+ err
175
+ });
176
+ (0, _helpers.wait)(this.reconnectTimeInSeconds * 1000).then(() => this._connect()) // `_connect()` should never throw.
177
+ .catch(neverThrows);
203
178
  });
204
- }
179
+ this._connecting = false;
180
+ this.emit('connect', {
181
+ connection,
182
+ url: urlString
183
+ });
184
+ return null;
185
+ });
186
+ }).catch(err => {
187
+ this.emit('disconnect', {
188
+ err
189
+ }); // Connection failed...
190
+
191
+ this._currentConnection = null; // TODO: Probably want to try right away here, especially if there are multiple brokers to try...
192
+
193
+ return (0, _helpers.wait)(this.reconnectTimeInSeconds * 1000).then(() => {
194
+ this._connecting = false;
195
+ return this._connect();
196
+ });
197
+ });
198
+ }
199
+
205
200
  }
201
+
206
202
  exports.default = AmqpConnectionManager;
207
203
  //# sourceMappingURL=AmqpConnectionManager.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/AmqpConnectionManager.js"],"names":["HEARTBEAT_IN_SECONDS","neverThrows","err","setImmediate","Error","message","stack","AmqpConnectionManager","EventEmitter","constructor","urls","options","findServers","_channels","_currentUrl","connectionOptions","heartbeatIntervalInSeconds","reconnectTimeInSeconds","setMaxListeners","_findServers","Promise","resolve","_connect","createChannel","channel","ChannelWrapper","push","once","filter","c","close","_closed","all","map","catch","then","_currentConnection","removeAllListeners","isConnected","_connecting","_urls","length","pb","callFn","Array","isArray","url","urlString","amqpUrl","urlUtils","parse","search","amqp","connect","format","connection","on","reason","emit"],"mappings":"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","file":"AmqpConnectionManager.js","sourcesContent":["import { EventEmitter } from 'events';\nimport amqp from 'amqplib';\nimport urlUtils from 'url';\n\nimport ChannelWrapper from './ChannelWrapper';\nimport { wait } from './helpers';\nimport pb from 'promise-breaker';\n\n// Default heartbeat time.\nconst HEARTBEAT_IN_SECONDS = 5;\n\n/* istanbul ignore next */\nfunction neverThrows() {\n return err =>\n setImmediate(() => {\n throw new Error(`AmqpConnectionManager - should never get here: ${err.message}\\n` +\n err.stack);\n });\n}\n\n//\n// Events:\n// * `connect({connection, url})` - Emitted whenever we connect to a broker.\n// * `disconnect({err})` - Emitted whenever we disconnect from a broker.\n//\nexport default class AmqpConnectionManager extends EventEmitter {\n /**\n * Create a new AmqplibConnectionManager.\n *\n * @param {(string|Object)[]} urls - An array of brokers to connect to.\n * Takes url strings or objects {url: string, connectionOptions?: object}\n * If present, a broker's [connectionOptions] will be used instead\n * of [options.connectionOptions] when passed to the amqplib connect method.\n * AmqplibConnectionManager will round-robin between them whenever it\n * needs to create a new connection.\n * @param {Object} [options={}] -\n * @param {number} [options.heartbeatIntervalInSeconds=5] - The interval,\n * in seconds, to send heartbeats.\n * @param {number} [options.reconnectTimeInSeconds] - The time to wait\n * before trying to reconnect. If not specified, defaults to\n * `heartbeatIntervalInSeconds`.\n * @param {Object} [options.connectionOptions] - Passed to the amqplib\n * connect method.\n * @param {function} [options.findServers] - A `fn(callback)` or a `fn()`\n * which returns a Promise. This should resolve to one or more servers\n * to connect to, either a single URL or an array of URLs. This is handy\n * when you're using a service discovery mechanism such as Consul or etcd.\n * Note that if this is supplied, then `urls` is ignored.\n */\n constructor(urls, options = {}) {\n super();\n if(!urls && !options.findServers) {\n throw new Error(\"Must supply either `urls` or `findServers`\");\n }\n this._channels = [];\n\n this._currentUrl = 0;\n this.connectionOptions = options.connectionOptions;\n\n this.heartbeatIntervalInSeconds = options.heartbeatIntervalInSeconds || HEARTBEAT_IN_SECONDS;\n this.reconnectTimeInSeconds = options.reconnectTimeInSeconds || this.heartbeatIntervalInSeconds;\n\n // There will be one listener per channel, and there could be a lot of channels, so disable warnings from node.\n this.setMaxListeners(0);\n\n this._findServers = options.findServers || (() => Promise.resolve(urls));\n\n this._connect();\n }\n\n // `options` here are any options that can be passed to ChannelWrapper.\n createChannel(options = {}) {\n const channel = new ChannelWrapper(this, options);\n this._channels.push(channel);\n channel.once('close', () => {\n this._channels = this._channels.filter(c => c !== channel);\n });\n return channel;\n }\n\n close() {\n if(this._closed) { return Promise.resolve(); }\n this._closed = true;\n\n return Promise.all(this._channels.map(channel => channel.close()))\n .catch(function() {\n // Ignore errors closing channels.\n })\n .then(() => {\n this._channels = [];\n if(this._currentConnection) {\n this._currentConnection.removeAllListeners('close');\n this._currentConnection.close();\n }\n this._currentConnection = null;\n });\n }\n\n isConnected() {\n return !!this._currentConnection;\n }\n\n _connect() {\n if(this._closed || this._connecting || this.isConnected()) {\n return Promise.resolve();\n }\n\n this._connecting = true;\n\n return Promise.resolve()\n .then(() => {\n if(!this._urls || (this._currentUrl >= this._urls.length)) {\n this._currentUrl = 0;\n return pb.callFn(this._findServers, 0, null);\n } else {\n return this._urls;\n }\n })\n .then(urls => {\n if(urls && !Array.isArray(urls)) { urls = [urls]; }\n this._urls = urls;\n\n if(!urls || (urls.length === 0)) {\n throw new Error('amqp-connection-manager: No servers found');\n }\n\n // Round robin between brokers\n const url = urls[this._currentUrl];\n this._currentUrl++;\n\n // url can be a string or object {url: string, connectionOptions?: object}\n const urlString = url.url || url;\n const connectionOptions = url.connectionOptions || this.connectionOptions;\n\n const amqpUrl = urlUtils.parse(urlString);\n if(amqpUrl.search) {\n amqpUrl.search += `&heartbeat=${this.heartbeatIntervalInSeconds}`;\n } else {\n amqpUrl.search = `?heartbeat=${this.heartbeatIntervalInSeconds}`;\n }\n\n return amqp.connect(urlUtils.format(amqpUrl), connectionOptions)\n .then(connection => {\n this._currentConnection = connection;\n\n //emit 'blocked' when RabbitMQ server decides to block the connection (resources running low)\n connection.on('blocked', reason => this.emit('blocked', { reason }));\n\n connection.on('unblocked', () => this.emit('unblocked'));\n\n // Reconnect if the broker goes away.\n connection.on('error', err =>\n Promise.resolve()\n .then(() =>this._currentConnection.close())\n .catch(() => { /* Ignore */ })\n .then(() => {\n this._currentConnection = null;\n this.emit('disconnect', { err });\n return this._connect();\n })\n // `_connect()` should never throw.\n .catch(neverThrows)\n );\n\n // Reconnect if the connection closes gracefully\n connection.on('close', err => {\n this._currentConnection = null;\n this.emit('disconnect', { err });\n\n wait(this.reconnectTimeInSeconds * 1000)\n .then(() => this._connect())\n // `_connect()` should never throw.\n .catch(neverThrows);\n });\n\n this._connecting = false;\n this.emit('connect', { connection, url: urlString });\n\n return null;\n });\n })\n .catch(err => {\n this.emit('disconnect', { err });\n\n // Connection failed...\n this._currentConnection = null;\n\n // TODO: Probably want to try right away here, especially if there are multiple brokers to try...\n return wait(this.reconnectTimeInSeconds * 1000)\n .then(() => {\n this._connecting = false;\n return this._connect();\n });\n });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/AmqpConnectionManager.js"],"names":["HEARTBEAT_IN_SECONDS","neverThrows","err","setImmediate","Error","message","stack","AmqpConnectionManager","EventEmitter","constructor","urls","options","findServers","_channels","_currentUrl","connectionOptions","heartbeatIntervalInSeconds","reconnectTimeInSeconds","setMaxListeners","_findServers","Promise","resolve","_connect","createChannel","channel","ChannelWrapper","push","once","filter","c","close","_closed","all","map","catch","then","_currentConnection","removeAllListeners","isConnected","_connecting","_urls","length","pb","callFn","Array","isArray","url","urlString","amqpUrl","urlUtils","parse","search","amqp","connect","format","connection","on","reason","emit"],"mappings":"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","sourcesContent":["import { EventEmitter } from 'events';\nimport amqp from 'amqplib';\nimport urlUtils from 'url';\n\nimport ChannelWrapper from './ChannelWrapper';\nimport { wait } from './helpers';\nimport pb from 'promise-breaker';\n\n// Default heartbeat time.\nconst HEARTBEAT_IN_SECONDS = 5;\n\n/* istanbul ignore next */\nfunction neverThrows() {\n return err =>\n setImmediate(() => {\n throw new Error(`AmqpConnectionManager - should never get here: ${err.message}\\n` +\n err.stack);\n });\n}\n\n//\n// Events:\n// * `connect({connection, url})` - Emitted whenever we connect to a broker.\n// * `disconnect({err})` - Emitted whenever we disconnect from a broker.\n//\nexport default class AmqpConnectionManager extends EventEmitter {\n /**\n * Create a new AmqplibConnectionManager.\n *\n * @param {(string|Object)[]} urls - An array of brokers to connect to.\n * Takes url strings or objects {url: string, connectionOptions?: object}\n * If present, a broker's [connectionOptions] will be used instead\n * of [options.connectionOptions] when passed to the amqplib connect method.\n * AmqplibConnectionManager will round-robin between them whenever it\n * needs to create a new connection.\n * @param {Object} [options={}] -\n * @param {number} [options.heartbeatIntervalInSeconds=5] - The interval,\n * in seconds, to send heartbeats.\n * @param {number} [options.reconnectTimeInSeconds] - The time to wait\n * before trying to reconnect. If not specified, defaults to\n * `heartbeatIntervalInSeconds`.\n * @param {Object} [options.connectionOptions] - Passed to the amqplib\n * connect method.\n * @param {function} [options.findServers] - A `fn(callback)` or a `fn()`\n * which returns a Promise. This should resolve to one or more servers\n * to connect to, either a single URL or an array of URLs. This is handy\n * when you're using a service discovery mechanism such as Consul or etcd.\n * Note that if this is supplied, then `urls` is ignored.\n */\n constructor(urls, options = {}) {\n super();\n if(!urls && !options.findServers) {\n throw new Error(\"Must supply either `urls` or `findServers`\");\n }\n this._channels = [];\n\n this._currentUrl = 0;\n this.connectionOptions = options.connectionOptions;\n\n this.heartbeatIntervalInSeconds = options.heartbeatIntervalInSeconds || HEARTBEAT_IN_SECONDS;\n this.reconnectTimeInSeconds = options.reconnectTimeInSeconds || this.heartbeatIntervalInSeconds;\n\n // There will be one listener per channel, and there could be a lot of channels, so disable warnings from node.\n this.setMaxListeners(0);\n\n this._findServers = options.findServers || (() => Promise.resolve(urls));\n\n this._connect();\n }\n\n // `options` here are any options that can be passed to ChannelWrapper.\n createChannel(options = {}) {\n const channel = new ChannelWrapper(this, options);\n this._channels.push(channel);\n channel.once('close', () => {\n this._channels = this._channels.filter(c => c !== channel);\n });\n return channel;\n }\n\n close() {\n if(this._closed) { return Promise.resolve(); }\n this._closed = true;\n\n return Promise.all(this._channels.map(channel => channel.close()))\n .catch(function() {\n // Ignore errors closing channels.\n })\n .then(() => {\n this._channels = [];\n if(this._currentConnection) {\n this._currentConnection.removeAllListeners('close');\n this._currentConnection.close();\n }\n this._currentConnection = null;\n });\n }\n\n isConnected() {\n return !!this._currentConnection;\n }\n\n _connect() {\n if(this._closed || this._connecting || this.isConnected()) {\n return Promise.resolve();\n }\n\n this._connecting = true;\n\n return Promise.resolve()\n .then(() => {\n if(!this._urls || (this._currentUrl >= this._urls.length)) {\n this._currentUrl = 0;\n return pb.callFn(this._findServers, 0, null);\n } else {\n return this._urls;\n }\n })\n .then(urls => {\n if(urls && !Array.isArray(urls)) { urls = [urls]; }\n this._urls = urls;\n\n if(!urls || (urls.length === 0)) {\n throw new Error('amqp-connection-manager: No servers found');\n }\n\n // Round robin between brokers\n const url = urls[this._currentUrl];\n this._currentUrl++;\n\n // url can be a string or object {url: string, connectionOptions?: object}\n const urlString = url.url || url;\n const connectionOptions = url.connectionOptions || this.connectionOptions;\n\n const amqpUrl = urlUtils.parse(urlString);\n if(amqpUrl.search) {\n amqpUrl.search += `&heartbeat=${this.heartbeatIntervalInSeconds}`;\n } else {\n amqpUrl.search = `?heartbeat=${this.heartbeatIntervalInSeconds}`;\n }\n\n return amqp.connect(urlUtils.format(amqpUrl), connectionOptions)\n .then(connection => {\n this._currentConnection = connection;\n\n //emit 'blocked' when RabbitMQ server decides to block the connection (resources running low)\n connection.on('blocked', reason => this.emit('blocked', { reason }));\n\n connection.on('unblocked', () => this.emit('unblocked'));\n\n connection.on('error', (/* err */) => {\n // if this event was emitted, then the connection was already closed,\n // so no need to call #close here\n // also, 'close' is emitted after 'error',\n // so no need for work already done in 'close' handler\n });\n\n // Reconnect if the connection closes\n connection.on('close', err => {\n this._currentConnection = null;\n this.emit('disconnect', { err });\n\n wait(this.reconnectTimeInSeconds * 1000)\n .then(() => this._connect())\n // `_connect()` should never throw.\n .catch(neverThrows);\n });\n\n this._connecting = false;\n this.emit('connect', { connection, url: urlString });\n\n return null;\n });\n })\n .catch(err => {\n this.emit('disconnect', { err });\n\n // Connection failed...\n this._currentConnection = null;\n\n // TODO: Probably want to try right away here, especially if there are multiple brokers to try...\n return wait(this.reconnectTimeInSeconds * 1000)\n .then(() => {\n this._connecting = false;\n return this._connect();\n });\n });\n }\n}\n"],"file":"AmqpConnectionManager.js"}