amqp-connection-manager 2.1.2 → 2.3.2
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/.babelrc +1 -1
- package/.mocharc.js +8 -0
- package/.nyc_output/{551b181965cc50f9defc88a59f86864e.json → 4b04f886-5873-4318-93c4-2f5f1a3b588b.json} +0 -0
- package/.nyc_output/b8bdc4e3-c464-4fd7-bb0d-b4d098a41917.json +1 -0
- package/.nyc_output/c34a8d23-3fcc-495b-9abe-2355d937cdf4.json +1 -0
- package/.nyc_output/processinfo/4b04f886-5873-4318-93c4-2f5f1a3b588b.json +1 -0
- package/.nyc_output/processinfo/b8bdc4e3-c464-4fd7-bb0d-b4d098a41917.json +1 -0
- package/.nyc_output/processinfo/c34a8d23-3fcc-495b-9abe-2355d937cdf4.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.nycrc +1 -1
- package/.travis.yml +0 -2
- package/CHANGELOG.md +28 -0
- package/README.md +53 -43
- package/lib/AmqpConnectionManager.js +167 -171
- package/lib/AmqpConnectionManager.js.map +1 -1
- package/lib/ChannelWrapper.js +356 -319
- package/lib/ChannelWrapper.js.map +1 -1
- package/lib/helpers.js +5 -4
- package/lib/helpers.js.map +1 -1
- package/lib/index.js +8 -9
- package/lib/index.js.map +1 -1
- package/package.json +18 -17
- package/.nyc_output/97c040dc7bab889b7f392acbe77d77fc.json +0 -1
- package/.nyc_output/aa87043a0d29f69ae42edb07706d556a.json +0 -1
package/README.md
CHANGED
|
@@ -7,14 +7,11 @@
|
|
|
7
7
|
[](https://david-dm.org/benbria/node-amqp-connection-manager#info=devDependencies)
|
|
8
8
|
[](https://david-dm.org/benbria/node-amqp-connection-manager#info=peerDependencies)
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
Connection management for amqplib.
|
|
12
11
|
|
|
13
|
-
amqp-connection-manager
|
|
14
|
-
=======================
|
|
12
|
+
# amqp-connection-manager
|
|
15
13
|
|
|
16
|
-
Features
|
|
17
|
-
---------
|
|
14
|
+
## Features
|
|
18
15
|
|
|
19
16
|
* Automatically reconnect when your amqplib broker dies in a fire.
|
|
20
17
|
* Round-robin connections between multiple brokers in a cluster.
|
|
@@ -22,24 +19,26 @@ Features:
|
|
|
22
19
|
* Supports both promises and callbacks (using [promise-breaker](https://github.com/jwalton/node-promise-breaker))
|
|
23
20
|
* Very un-opinionated library - a thin wrapper around amqplib.
|
|
24
21
|
|
|
25
|
-
Installation
|
|
26
|
-
-------------
|
|
22
|
+
## Installation
|
|
27
23
|
|
|
28
24
|
npm install --save amqplib amqp-connection-manager
|
|
29
25
|
|
|
30
|
-
Basics
|
|
31
|
-
-------
|
|
26
|
+
## Basics
|
|
32
27
|
|
|
33
|
-
The basic idea here is that, usually, when you create a new channel, you do some
|
|
34
|
-
|
|
35
|
-
you
|
|
28
|
+
The basic idea here is that, usually, when you create a new channel, you do some
|
|
29
|
+
setup work at the beginning (like asserting that various queues or exchanges
|
|
30
|
+
exist, or binding to queues), and then you send and receive messages and you
|
|
31
|
+
never touch that stuff again.
|
|
36
32
|
|
|
37
|
-
amqp-connection-manager will reconnect to a new broker whenever the broker it is
|
|
38
|
-
ask amqp-connection-manager for a
|
|
39
|
-
|
|
33
|
+
amqp-connection-manager will reconnect to a new broker whenever the broker it is
|
|
34
|
+
currently connected to dies. When you ask amqp-connection-manager for a
|
|
35
|
+
channel, you specify one or more `setup` functions to run; the setup functions
|
|
36
|
+
will be run every time amqp-connection-manager reconnects, to make sure your
|
|
37
|
+
channel and broker are in a sane state.
|
|
40
38
|
|
|
41
|
-
Before we get into an example, note this example is written using Promises,
|
|
42
|
-
function which returns a Promise will also accept
|
|
39
|
+
Before we get into an example, note this example is written using Promises,
|
|
40
|
+
however much like amqplib, any function which returns a Promise will also accept
|
|
41
|
+
a callback as an optional parameter.
|
|
43
42
|
|
|
44
43
|
Here's the example:
|
|
45
44
|
|
|
@@ -49,13 +48,14 @@ var amqp = require('amqp-connection-manager');
|
|
|
49
48
|
// Create a new connection manager
|
|
50
49
|
var connection = amqp.connect(['amqp://localhost']);
|
|
51
50
|
|
|
52
|
-
// Ask the connection manager for a ChannelWrapper. Specify a setup function to
|
|
53
|
-
// to the broker.
|
|
51
|
+
// Ask the connection manager for a ChannelWrapper. Specify a setup function to
|
|
52
|
+
// run every time we reconnect to the broker.
|
|
54
53
|
var channelWrapper = connection.createChannel({
|
|
55
54
|
json: true,
|
|
56
55
|
setup: function(channel) {
|
|
57
56
|
// `channel` here is a regular amqplib `ConfirmChannel`.
|
|
58
|
-
|
|
57
|
+
// Note that `this` here is the channelWrapper instance.
|
|
58
|
+
return channel.assertQueue('rxQueueName', {durable: true});
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
|
|
@@ -64,15 +64,16 @@ var channelWrapper = connection.createChannel({
|
|
|
64
64
|
// when the message is actually sent (or not sent.)
|
|
65
65
|
channelWrapper.sendToQueue('rxQueueName', {hello: 'world'})
|
|
66
66
|
.then(function() {
|
|
67
|
-
|
|
67
|
+
return console.log("Message was sent! Hooray!");
|
|
68
68
|
}).catch(function(err) {
|
|
69
|
-
|
|
69
|
+
return console.log("Message was rejected... Boo!");
|
|
70
70
|
});
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
Sometimes it's handy to modify a channel at run time. For example, suppose you
|
|
74
|
-
one kind of message, and you decide you now
|
|
75
|
-
|
|
73
|
+
Sometimes it's handy to modify a channel at run time. For example, suppose you
|
|
74
|
+
have a channel that's listening to one kind of message, and you decide you now
|
|
75
|
+
also want to listen to some other kind of message. This can be done by adding a
|
|
76
|
+
new setup function to an existing ChannelWrapper:
|
|
76
77
|
|
|
77
78
|
```js
|
|
78
79
|
channelWrapper.addSetup(function(channel) {
|
|
@@ -84,21 +85,24 @@ channelWrapper.addSetup(function(channel) {
|
|
|
84
85
|
});
|
|
85
86
|
```
|
|
86
87
|
|
|
87
|
-
`addSetup()` returns a Promise which resolves when the setup function is
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
`addSetup()` returns a Promise which resolves when the setup function is
|
|
89
|
+
finished (or immediately, if the underlying connection is not currently
|
|
90
|
+
connected to a broker.) There is also a `removeSetup(setup, teardown)` which
|
|
91
|
+
will run `teardown(channel)` if the channel is currently connected to a broker
|
|
92
|
+
(and will not run `teardown` at all otherwise.) Note that `setup` and `teardown`
|
|
93
|
+
*must* either accept a callback or return a Promise.
|
|
91
94
|
|
|
92
95
|
See a complete example in the [examples](./examples) folder.
|
|
93
96
|
|
|
94
|
-
API
|
|
95
|
-
----
|
|
97
|
+
## API
|
|
96
98
|
|
|
97
99
|
### connect(urls, options)
|
|
100
|
+
|
|
98
101
|
Creates a new AmqpConnectionManager, which will connect to one of the URLs provided in `urls`. If a broker is
|
|
99
102
|
unreachable or dies, then AmqpConnectionManager will try the next available broker, round-robin.
|
|
100
103
|
|
|
101
104
|
Options:
|
|
105
|
+
|
|
102
106
|
* `options.heartbeatIntervalInSeconds` - Interval to send heartbeats to broker. Defaults to 5 seconds.
|
|
103
107
|
* `options.reconnectTimeInSeconds` - The time to wait before trying to reconnect. If not specified,
|
|
104
108
|
defaults to `heartbeatIntervalInSeconds`.
|
|
@@ -109,37 +113,43 @@ Options:
|
|
|
109
113
|
* `options.connectionOptions` is passed as options to the amqplib connect method.
|
|
110
114
|
|
|
111
115
|
### AmqpConnectionManager events
|
|
116
|
+
|
|
112
117
|
* `connect({connection, url})` - Emitted whenever we successfully connect to a broker.
|
|
113
118
|
* `disconnect({err})` - Emitted whenever we disconnect from a broker.
|
|
114
119
|
|
|
115
|
-
|
|
116
120
|
### AmqpConnectionManager#createChannel(options)
|
|
121
|
+
|
|
117
122
|
Create a new ChannelWrapper. This is a proxy for the actual channel (which may or may not exist at any moment,
|
|
118
123
|
depending on whether or not we are currently connected.)
|
|
119
124
|
|
|
120
125
|
Options:
|
|
126
|
+
|
|
121
127
|
* `options.name` - Name for this channel. Used for debugging.
|
|
122
|
-
* `options.setup(channel, [cb])` - A function to call whenever we reconnect to the
|
|
123
|
-
|
|
128
|
+
* `options.setup(channel, [cb])` - A function to call whenever we reconnect to the
|
|
129
|
+
broker (and therefore create a new underlying channel.) This function should
|
|
130
|
+
either accept a callback, or return a Promise. See `addSetup` below.
|
|
131
|
+
Note that `this` inside the setup function will the returned ChannelWrapper.
|
|
132
|
+
The ChannelWrapper has a special `context` member you can use to store
|
|
133
|
+
arbitrary data in.
|
|
124
134
|
* `options.json` if true, then ChannelWrapper assumes all messages passed to `publish()` and `sendToQueue()`
|
|
125
135
|
are plain JSON objects. These will be encoded automatically before being sent.
|
|
126
136
|
|
|
127
|
-
|
|
128
137
|
### AmqpConnectionManager#isConnected()
|
|
129
|
-
Returns true if the AmqpConnectionManager is connected to a broker, false otherwise.
|
|
130
138
|
|
|
139
|
+
Returns true if the AmqpConnectionManager is connected to a broker, false otherwise.
|
|
131
140
|
|
|
132
141
|
### AmqpConnectionManager#close()
|
|
133
|
-
Close this AmqpConnectionManager and free all associated resources.
|
|
134
142
|
|
|
143
|
+
Close this AmqpConnectionManager and free all associated resources.
|
|
135
144
|
|
|
136
145
|
### ChannelWrapper events
|
|
146
|
+
|
|
137
147
|
* `connect` - emitted every time this channel connects or reconnects.
|
|
138
148
|
* `error(err, {name})` - emitted if an error occurs setting up the channel.
|
|
139
149
|
* `close` - emitted when this channel closes via a call to `close()`
|
|
140
150
|
|
|
141
|
-
|
|
142
151
|
### ChannelWrapper#addSetup(setup)
|
|
152
|
+
|
|
143
153
|
Adds a new 'setup handler'.
|
|
144
154
|
|
|
145
155
|
`setup(channel, [cb])` is a function to call when a new underlying channel is created - handy for asserting
|
|
@@ -155,27 +165,27 @@ reconnects, even if it throws an error.)
|
|
|
155
165
|
Setup functions should, ideally, not throw errors, but if they do then the ChannelWrapper will emit an 'error'
|
|
156
166
|
event.
|
|
157
167
|
|
|
158
|
-
|
|
159
168
|
### ChannelWrapper#removeSetup(setup, teardown)
|
|
169
|
+
|
|
160
170
|
Removes a setup handler. If the channel is currently connected, will call `teardown(channel)`, passing in the
|
|
161
171
|
underlying amqplib ConfirmChannel. `teardown` should either take a callback or return a Promise.
|
|
162
172
|
|
|
163
|
-
|
|
164
173
|
### ChannelWrapper#publish and ChannelWrapper#sendToQueue
|
|
174
|
+
|
|
165
175
|
These work exactly like their counterparts in amqplib's Channel, except that they return a Promise (or accept a
|
|
166
176
|
callback) which resolves when the message is confirmed to have been delivered to the broker. The promise rejects if
|
|
167
177
|
either the broker refuses the message, or if `close()` is called on the ChannelWrapper before the message can be
|
|
168
178
|
delivered.
|
|
169
179
|
|
|
170
|
-
|
|
171
180
|
### ChannelWrapper#ack and ChannelWrapper#nack
|
|
181
|
+
|
|
172
182
|
These are just aliases for calling `ack()` and `nack()` on the underlying channel. They do nothing if the underlying
|
|
173
183
|
channel is not connected.
|
|
174
184
|
|
|
175
|
-
|
|
176
185
|
### ChannelWrapper#queueLength()
|
|
177
|
-
Returns a count of messages currently waiting to be sent to the underlying channel.
|
|
178
186
|
|
|
187
|
+
Returns a count of messages currently waiting to be sent to the underlying channel.
|
|
179
188
|
|
|
180
189
|
### ChannelWrapper#close()
|
|
190
|
+
|
|
181
191
|
Close a channel, clean up resources associated with it.
|
|
@@ -1,207 +1,203 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
|
|
4
|
+
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.default = void 0;
|
|
6
7
|
|
|
7
|
-
var _events = require(
|
|
8
|
+
var _events = require("events");
|
|
8
9
|
|
|
9
|
-
var _amqplib = require(
|
|
10
|
+
var _amqplib = _interopRequireDefault(require("amqplib"));
|
|
10
11
|
|
|
11
|
-
var
|
|
12
|
+
var _url = _interopRequireDefault(require("url"));
|
|
12
13
|
|
|
13
|
-
var
|
|
14
|
+
var _ChannelWrapper = _interopRequireDefault(require("./ChannelWrapper"));
|
|
14
15
|
|
|
15
|
-
var
|
|
16
|
+
var _helpers = require("./helpers");
|
|
16
17
|
|
|
17
|
-
var
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
this.reconnectTimeInSeconds = options.reconnectTimeInSeconds || this.heartbeatIntervalInSeconds;
|
|
74
|
+
this.setMaxListeners(0);
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
this.setMaxListeners(0);
|
|
76
|
+
this._findServers = options.findServers || (() => Promise.resolve(urls));
|
|
83
77
|
|
|
84
|
-
|
|
78
|
+
this._connect();
|
|
79
|
+
} // `options` here are any options that can be passed to ChannelWrapper.
|
|
85
80
|
|
|
86
|
-
this._connect();
|
|
87
|
-
}
|
|
88
81
|
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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"}
|