rascal 13.1.3 → 14.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc.json +4 -1
- package/CHANGELOG.md +23 -0
- package/README.md +200 -209
- package/examples/advanced/cluster.js +4 -4
- package/examples/advanced/config.js +45 -50
- package/examples/advanced/handlers/deleteUser.js +9 -16
- package/examples/advanced/handlers/saveUser.js +11 -18
- package/examples/advanced/index.js +81 -103
- package/examples/busy-publisher/config.json +40 -0
- package/examples/busy-publisher/index.js +14 -20
- package/examples/default-exchange/config.json +25 -0
- package/examples/default-exchange/index.js +10 -15
- package/examples/mocha/config.json +21 -0
- package/examples/mocha/test.js +16 -24
- package/examples/promises/config.json +27 -0
- package/examples/promises/index.js +9 -14
- package/examples/simple/config.json +37 -0
- package/examples/simple/index.js +11 -15
- package/index.js +6 -6
- package/lib/amqp/Broker.js +65 -95
- package/lib/amqp/BrokerAsPromised.js +7 -16
- package/lib/amqp/Publication.js +72 -212
- package/lib/amqp/PublicationSession.js +8 -8
- package/lib/amqp/SubscriberError.js +107 -233
- package/lib/amqp/SubscriberSession.js +56 -76
- package/lib/amqp/SubscriberSessionAsPromised.js +3 -3
- package/lib/amqp/Subscription.js +96 -313
- package/lib/amqp/Vhost.js +120 -265
- package/lib/amqp/tasks/applyBindings.js +12 -42
- package/lib/amqp/tasks/assertExchanges.js +6 -11
- package/lib/amqp/tasks/assertQueues.js +4 -4
- package/lib/amqp/tasks/assertVhost.js +6 -4
- package/lib/amqp/tasks/checkExchanges.js +4 -4
- package/lib/amqp/tasks/checkQueues.js +4 -4
- package/lib/amqp/tasks/checkVhost.js +6 -4
- package/lib/amqp/tasks/closeChannel.js +3 -3
- package/lib/amqp/tasks/closeConnection.js +3 -3
- package/lib/amqp/tasks/createChannel.js +3 -3
- package/lib/amqp/tasks/createConnection.js +38 -53
- package/lib/amqp/tasks/deleteExchanges.js +5 -5
- package/lib/amqp/tasks/deleteQueues.js +4 -4
- package/lib/amqp/tasks/deleteVhost.js +12 -16
- package/lib/amqp/tasks/index.js +25 -25
- package/lib/amqp/tasks/initCounters.js +5 -6
- package/lib/amqp/tasks/initPublications.js +4 -4
- package/lib/amqp/tasks/initShovels.js +15 -20
- package/lib/amqp/tasks/initSubscriptions.js +5 -11
- package/lib/amqp/tasks/initVhosts.js +8 -8
- package/lib/amqp/tasks/purgeQueues.js +4 -4
- package/lib/backoff/exponential.js +6 -8
- package/lib/backoff/index.js +2 -2
- package/lib/backoff/linear.js +3 -3
- package/lib/config/baseline.js +15 -16
- package/lib/config/configure.js +68 -193
- package/lib/config/fqn.js +3 -3
- package/lib/config/schema.json +686 -0
- package/lib/config/tests.js +3 -3
- package/lib/config/validate.js +87 -458
- package/lib/counters/inMemory.js +3 -3
- package/lib/counters/inMemoryCluster.js +16 -23
- package/lib/counters/index.js +3 -3
- package/lib/management/Client.js +55 -0
- package/package.json +2 -1
- package/examples/busy-publisher/config.js +0 -39
- package/examples/default-exchange/config.js +0 -24
- package/examples/mocha/config.js +0 -20
- package/examples/promises/config.js +0 -26
- package/examples/simple/config.js +0 -36
- package/lib/management/client.js +0 -90
package/README.md
CHANGED
|
@@ -7,9 +7,7 @@ Rascal is a rich pub/sub wrapper around [amqplib](https://www.npmjs.com/package/
|
|
|
7
7
|
[](https://github.com/guidesmiths/rascal/actions?query=workflow%3A%22Node.js+CI%22)
|
|
8
8
|
[](https://codeclimate.com/github/guidesmiths/rascal)
|
|
9
9
|
[](https://codeclimate.com/github/guidesmiths/rascal/coverage)
|
|
10
|
-
[](https://david-dm.org/guidesmiths/rascal)
|
|
12
|
-
[](https://david-dm.org/guidesmiths/rascal?type=dev)
|
|
10
|
+
[](https://github.com/prettier/prettier)
|
|
13
11
|
[](https://snyk.io/advisor/npm-package/rascal)
|
|
14
12
|
[](https://www.npmjs.com/package/zunit)
|
|
15
13
|
|
|
@@ -49,6 +47,10 @@ Rascal extends the existing [RabbitMQ Concepts](https://www.rabbitmq.com/tutoria
|
|
|
49
47
|
|
|
50
48
|
A **publication** is a named configuration for publishing a message, including the destination queue or exchange, routing configuration, encryption profile and reliability guarantees, message options, etc. A **subscription** is a named configuration for consuming messages, including the source queue, encryption profile, content encoding, delivery options (e.g. acknowledgement handling and prefetch), etc. These must be [configured](#configuration) and supplied when creating the Rascal broker. After the broker has been created the subscriptions and publications can be retrivied from the broker and used to publish and consume messages.
|
|
51
49
|
|
|
50
|
+
### Breaking Changes in Rascal@14
|
|
51
|
+
|
|
52
|
+
Rascal@15 waits for inflight messages to be acknowledged before closing subscriber channels. Prior to this version Rascal just waited an arbitary amount of time. If you application does not acknowledge a message for some reason (quite likely in tests) calling `subscription.cancel`, `broker.unsubscribeAll`, `broker.bounce`, `broker.shutdown` or `broker.nuke` will wait indefinitely. You can specify a `closeTimeout` in your subscription config, however if this is exceeded the `subscription.cancel` and `broker.unsubscribeAll` methods will yield an error, while the `broker.bounce`, `broker.shutdown` and `broker.nuke` methods will emit an error, but attempt to continue. In both cases the error will have a code of `ETIMEDOUT` and message stating `Callback function "waitForUnacknowledgedMessages" timed out`.
|
|
53
|
+
|
|
52
54
|
### Special Note
|
|
53
55
|
|
|
54
56
|
RabbitMQ 3.8.0 introduced [quorum queues](https://www.rabbitmq.com/quorum-queues.html). Although quorum queues may not be suitable in all situations, they provide [poison message handling](https://www.rabbitmq.com/quorum-queues.html#poison-message-handling) without the need for an external [redelivery counter](https://github.com/guidesmiths/rascal#dealing-with-redeliveries) and offer better data safety in the event of a network partition. You can read more about them [here](https://www.cloudamqp.com/blog/reasons-you-should-switch-to-quorum-queues.html) and [here](https://blog.rabbitmq.com/posts/2020/06/quorum-queues-local-delivery).
|
|
@@ -58,29 +60,26 @@ RabbitMQ 3.8.0 introduced [quorum queues](https://www.rabbitmq.com/quorum-queues
|
|
|
58
60
|
### Async/Await
|
|
59
61
|
|
|
60
62
|
```js
|
|
61
|
-
const Broker = require(
|
|
62
|
-
const config = require(
|
|
63
|
+
const Broker = require('rascal').BrokerAsPromised;
|
|
64
|
+
const config = require('./config');
|
|
63
65
|
|
|
64
66
|
(async () => {
|
|
65
67
|
try {
|
|
66
68
|
const broker = await Broker.create(config);
|
|
67
|
-
broker.on(
|
|
69
|
+
broker.on('error', console.error);
|
|
68
70
|
|
|
69
71
|
// Publish a message
|
|
70
|
-
const publication = await broker.publish(
|
|
71
|
-
|
|
72
|
-
"Hello World!"
|
|
73
|
-
);
|
|
74
|
-
publication.on("error", console.error);
|
|
72
|
+
const publication = await broker.publish('demo_publication', 'Hello World!');
|
|
73
|
+
publication.on('error', console.error);
|
|
75
74
|
|
|
76
75
|
// Consume a message
|
|
77
|
-
const subscription = await broker.subscribe(
|
|
76
|
+
const subscription = await broker.subscribe('demo_subscription');
|
|
78
77
|
subscription
|
|
79
|
-
.on(
|
|
78
|
+
.on('message', (message, content, ackOrNack) => {
|
|
80
79
|
console.log(content);
|
|
81
80
|
ackOrNack();
|
|
82
81
|
})
|
|
83
|
-
.on(
|
|
82
|
+
.on('error', console.error);
|
|
84
83
|
} catch (err) {
|
|
85
84
|
console.error(err);
|
|
86
85
|
}
|
|
@@ -90,29 +89,29 @@ const config = require("./config");
|
|
|
90
89
|
### Callbacks
|
|
91
90
|
|
|
92
91
|
```js
|
|
93
|
-
const Broker = require(
|
|
94
|
-
const config = require(
|
|
92
|
+
const Broker = require('rascal').Broker;
|
|
93
|
+
const config = require('./config');
|
|
95
94
|
|
|
96
95
|
Broker.create(config, (err, broker) => {
|
|
97
96
|
if (err) throw err;
|
|
98
97
|
|
|
99
|
-
broker.on(
|
|
98
|
+
broker.on('error', console.error);
|
|
100
99
|
|
|
101
100
|
// Publish a message
|
|
102
|
-
broker.publish(
|
|
101
|
+
broker.publish('demo_publication', 'Hello World!', (err, publication) => {
|
|
103
102
|
if (err) throw err;
|
|
104
|
-
publication.on(
|
|
103
|
+
publication.on('error', console.error);
|
|
105
104
|
});
|
|
106
105
|
|
|
107
106
|
// Consume a message
|
|
108
|
-
broker.subscribe(
|
|
107
|
+
broker.subscribe('demo_subscription', (err, subscription) => {
|
|
109
108
|
if (err) throw err;
|
|
110
109
|
subscription
|
|
111
|
-
.on(
|
|
110
|
+
.on('message', (message, content, ackOrNack) => {
|
|
112
111
|
console.log(content);
|
|
113
112
|
ackOrNack();
|
|
114
113
|
})
|
|
115
|
-
.on(
|
|
114
|
+
.on('error', console.error);
|
|
116
115
|
});
|
|
117
116
|
});
|
|
118
117
|
```
|
|
@@ -121,12 +120,13 @@ See [here](https://github.com/guidesmiths/rascal/tree/master/examples) for more
|
|
|
121
120
|
|
|
122
121
|
## Avoiding Potential Message Loss
|
|
123
122
|
|
|
124
|
-
There are
|
|
123
|
+
There are three situations when Rascal will nack a message without requeue, leading to potential data loss.
|
|
125
124
|
|
|
126
125
|
1. When it is unable to parse the message content and the subscriber has no 'invalid_content' listener
|
|
127
126
|
1. When the subscriber's (optional) redelivery limit has been exceeded and the subscriber has neither a 'redeliveries_error' nor a 'redeliveries_exceeded' listener
|
|
127
|
+
1. When attempting to recover by [republishing](#republishing), [forwarding](#forwarding), but the recovery operation fails.
|
|
128
128
|
|
|
129
|
-
The reason Rascal nacks the message is because the
|
|
129
|
+
The reason Rascal nacks the message is because the alternatives are to leave the message unacknowledged indefinitely, or to rollback and retry the message in an infinite tight loop. This can DDOS your application and cause problems for your infrastructure. Providing you have correctly configured dead letter queues and/or listen to the "invalid_content" and "redeliveries_exceeded" subscriber events, your messages should be safe.
|
|
130
130
|
|
|
131
131
|
## Very Important Section About Event Handling
|
|
132
132
|
|
|
@@ -135,8 +135,8 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
135
135
|
1. Immediately after obtaining a broker instance
|
|
136
136
|
|
|
137
137
|
```js
|
|
138
|
-
broker.on(
|
|
139
|
-
console.error(
|
|
138
|
+
broker.on('error', (err, { vhost, connectionUrl }) => {
|
|
139
|
+
console.error('Broker error', err, vhost, connectionUrl);
|
|
140
140
|
});
|
|
141
141
|
```
|
|
142
142
|
|
|
@@ -145,13 +145,13 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
145
145
|
```js
|
|
146
146
|
// Async/Await
|
|
147
147
|
try {
|
|
148
|
-
const subscription = await broker.subscribe(
|
|
148
|
+
const subscription = await broker.subscribe('s1');
|
|
149
149
|
subscription
|
|
150
|
-
.on(
|
|
150
|
+
.on('message', (message, content, ackOrNack) => {
|
|
151
151
|
// Do stuff with message
|
|
152
152
|
})
|
|
153
|
-
.on(
|
|
154
|
-
console.error(
|
|
153
|
+
.on('error', (err) => {
|
|
154
|
+
console.error('Subscriber error', err);
|
|
155
155
|
});
|
|
156
156
|
} catch (err) {
|
|
157
157
|
throw new Error(`Rascal config error: ${err.message}`);
|
|
@@ -160,14 +160,14 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
160
160
|
|
|
161
161
|
```js
|
|
162
162
|
// Callbacks
|
|
163
|
-
broker.subscribe(
|
|
163
|
+
broker.subscribe('s1', (err, subscription) => {
|
|
164
164
|
if (err) throw new Error(`Rascal config error: ${err.message}`);
|
|
165
165
|
subscription
|
|
166
|
-
.on(
|
|
166
|
+
.on('message', (message, content, ackOrNack) => {
|
|
167
167
|
// Do stuff with message
|
|
168
168
|
})
|
|
169
|
-
.on(
|
|
170
|
-
console.error(
|
|
169
|
+
.on('error', (err) => {
|
|
170
|
+
console.error('Subscriber error', err);
|
|
171
171
|
});
|
|
172
172
|
});
|
|
173
173
|
```
|
|
@@ -177,9 +177,9 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
177
177
|
```js
|
|
178
178
|
// Async/Await
|
|
179
179
|
try {
|
|
180
|
-
const publication = await broker.publish(
|
|
181
|
-
publication.on(
|
|
182
|
-
console.error(
|
|
180
|
+
const publication = await broker.publish('p1', 'some text');
|
|
181
|
+
publication.on('error', (err, messageId) => {
|
|
182
|
+
console.error('Publisher error', err, messageId);
|
|
183
183
|
});
|
|
184
184
|
} catch (err) {
|
|
185
185
|
throw new Error(`Rascal config error: ${err.message}`);
|
|
@@ -188,10 +188,10 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
188
188
|
|
|
189
189
|
```js
|
|
190
190
|
// Callbacks
|
|
191
|
-
broker.publish(
|
|
191
|
+
broker.publish('p1', 'some text', (err, publication) => {
|
|
192
192
|
if (err) throw new Error(`Rascal config error: ${err.message}`);
|
|
193
|
-
publication.on(
|
|
194
|
-
console.error(
|
|
193
|
+
publication.on('error', (err, messageId) => {
|
|
194
|
+
console.error('Publisher error', err, messageId);
|
|
195
195
|
});
|
|
196
196
|
});
|
|
197
197
|
```
|
|
@@ -201,9 +201,9 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
201
201
|
```js
|
|
202
202
|
// Async/Await
|
|
203
203
|
try {
|
|
204
|
-
const publication = await broker.forward(
|
|
205
|
-
publication.on(
|
|
206
|
-
console.error(
|
|
204
|
+
const publication = await broker.forward('p1', message);
|
|
205
|
+
publication.on('error', (err, messageId) => {
|
|
206
|
+
console.error('Publisher error', err, messageId);
|
|
207
207
|
});
|
|
208
208
|
} catch (err) {
|
|
209
209
|
throw new Error(`Rascal config error: ${err.message}`);
|
|
@@ -212,10 +212,10 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
212
212
|
|
|
213
213
|
```js
|
|
214
214
|
// Callbacks
|
|
215
|
-
broker.forward(
|
|
215
|
+
broker.forward('p1', message, (err, publication) => {
|
|
216
216
|
if (err) throw new Error(`Rascal config error: ${err.message}`);
|
|
217
|
-
publication.on(
|
|
218
|
-
console.error(
|
|
217
|
+
publication.on('error', (err, messageId) => {
|
|
218
|
+
console.error('Publisher error', err, messageId);
|
|
219
219
|
});
|
|
220
220
|
});
|
|
221
221
|
```
|
|
@@ -227,10 +227,8 @@ The reason Rascal nacks the message is because the alternative is to rollback an
|
|
|
227
227
|
The broker emits the `vhost_initialised` event after recovering from a connection error. An object containing the vhost name and connection url (with obfuscated password) are passed to he event handler. e.g.
|
|
228
228
|
|
|
229
229
|
```js
|
|
230
|
-
broker.on(
|
|
231
|
-
console.log(
|
|
232
|
-
`Vhost: ${vhost} was initialised using connection: ${connectionUrl}`
|
|
233
|
-
);
|
|
230
|
+
broker.on('vhost_initialised', ({ vhost, connectionUrl }) => {
|
|
231
|
+
console.log(`Vhost: ${vhost} was initialised using connection: ${connectionUrl}`);
|
|
234
232
|
});
|
|
235
233
|
```
|
|
236
234
|
|
|
@@ -239,15 +237,11 @@ broker.on("vhost_initialised", ({ vhost, connectionUrl }) => {
|
|
|
239
237
|
RabbitMQ notifies clients of [blocked and unblocked](https://www.rabbitmq.com/connection-blocked.html) connections, which rascal forwards from the connection to the broker. e.g.
|
|
240
238
|
|
|
241
239
|
```js
|
|
242
|
-
broker.on(
|
|
243
|
-
console.log(
|
|
244
|
-
`Vhost: ${vhost} was blocked using connection: ${connectionUrl}. Reason: ${reason}`
|
|
245
|
-
);
|
|
240
|
+
broker.on('blocked', (reason, { vhost, connectionUrl }) => {
|
|
241
|
+
console.log(`Vhost: ${vhost} was blocked using connection: ${connectionUrl}. Reason: ${reason}`);
|
|
246
242
|
});
|
|
247
|
-
broker.on(
|
|
248
|
-
console.log(
|
|
249
|
-
`Vhost: ${vhost} was unblocked using connection: ${connectionUrl}.`
|
|
250
|
-
);
|
|
243
|
+
broker.on('unblocked', ({ vhost, connectionUrl }) => {
|
|
244
|
+
console.log(`Vhost: ${vhost} was unblocked using connection: ${connectionUrl}.`);
|
|
251
245
|
});
|
|
252
246
|
```
|
|
253
247
|
|
|
@@ -256,16 +250,16 @@ broker.on("unblocked", ({ vhost, connectionUrl }) => {
|
|
|
256
250
|
Rascal is highly configurable, but ships with what we consider to be sensible defaults (optimised for reliability rather than speed) for production and test environments.
|
|
257
251
|
|
|
258
252
|
```js
|
|
259
|
-
var rascal = require(
|
|
260
|
-
var definitions = require(
|
|
253
|
+
var rascal = require('rascal');
|
|
254
|
+
var definitions = require('./your-config.json');
|
|
261
255
|
var config = rascal.withDefaultConfig(definitions);
|
|
262
256
|
```
|
|
263
257
|
|
|
264
258
|
or
|
|
265
259
|
|
|
266
260
|
```js
|
|
267
|
-
var rascal = require(
|
|
268
|
-
var definitions = require(
|
|
261
|
+
var rascal = require('rascal');
|
|
262
|
+
var definitions = require('./your-test-config.json');
|
|
269
263
|
var config = rascal.withTestConfig(definitions);
|
|
270
264
|
```
|
|
271
265
|
|
|
@@ -279,10 +273,11 @@ The most common configuration options are
|
|
|
279
273
|
- [publications](#publications)
|
|
280
274
|
- [subscriptions](#subscriptions)
|
|
281
275
|
|
|
282
|
-
A simple configuration is shown below.
|
|
276
|
+
A simple configuration is shown below. You can reference Rascal's JSON schema from the config to enable validation and suggestions in compatible IDEs.
|
|
283
277
|
|
|
284
278
|
```json
|
|
285
279
|
{
|
|
280
|
+
"$schema": "./node_modules/rascal/lib/config/schema.json",
|
|
286
281
|
"vhosts": {
|
|
287
282
|
"/": {
|
|
288
283
|
"connection": {
|
|
@@ -416,11 +411,7 @@ If you specify an array of connections instead of a single connection object Ras
|
|
|
416
411
|
"vhosts": {
|
|
417
412
|
"v1": {
|
|
418
413
|
"connectionStrategy": "random",
|
|
419
|
-
"connections": [
|
|
420
|
-
"amqp://guest:guest@broker1.example.com:5672/v1?heartbeat=10",
|
|
421
|
-
"amqp://guest:guest@broker2.example.com:5672/v1?heartbeat=10",
|
|
422
|
-
"amqp://guest:guest@broker3.example.com:5672/v1?heartbeat=10"
|
|
423
|
-
]
|
|
414
|
+
"connections": ["amqp://guest:guest@broker1.example.com:5672/v1?heartbeat=10", "amqp://guest:guest@broker2.example.com:5672/v1?heartbeat=10", "amqp://guest:guest@broker3.example.com:5672/v1?heartbeat=10"]
|
|
424
415
|
}
|
|
425
416
|
}
|
|
426
417
|
}
|
|
@@ -493,6 +484,15 @@ Rascal uses [superagent](https://github.com/visionmedia/superagent) under the ho
|
|
|
493
484
|
}
|
|
494
485
|
```
|
|
495
486
|
|
|
487
|
+
You can also supply your own agent via the broker components. Use this when you need to set [TLS options](https://visionmedia.github.io/superagent/#tls-options).
|
|
488
|
+
|
|
489
|
+
```js
|
|
490
|
+
const superagent = require('superagent-defaults');
|
|
491
|
+
const agent = superagent().on('request', (req) => console.log(req.url));
|
|
492
|
+
const components = { agent };
|
|
493
|
+
const broker = await Broker.create(config, components);
|
|
494
|
+
```
|
|
495
|
+
|
|
496
496
|
#### assert
|
|
497
497
|
|
|
498
498
|
When set to true, Rascal will create the vhost if one doesn't exist using the RabbitMQ management API. This requires the [management plugin](https://www.rabbitmq.com/management.html) to be installed on the broker and for the management user to have necessary permissions.
|
|
@@ -557,21 +557,15 @@ Unfortunately there is a [bug](https://github.com/coopernurse/node-pool/issues/1
|
|
|
557
557
|
[amqplib flow control](https://www.squaremobius.net/amqp.node/channel_api.html#flowcontrol) dictates channels act like stream.Writable when Rascal calls `channel.publish` or `channel.sendToQueue`, returning false when the channel is saturated and true if it is not. While it is possible to ignore this and keep publishing messages, it is preferable to apply back pressure to the message source. You can do this by listening to the broker `busy` and `ready` events. Busy events are emitted when the number of outstanding channel requests reach the pool max size, and ready events emitted when the outstanding channel requests falls back down to zero. The pool details are passed to both event handlers so you can take selective action.
|
|
558
558
|
|
|
559
559
|
```js
|
|
560
|
-
broker.on(
|
|
561
|
-
|
|
562
|
-
({
|
|
563
|
-
|
|
564
|
-
console.warn(`vhost ${vhost} is busy`);
|
|
565
|
-
}
|
|
566
|
-
);
|
|
560
|
+
broker.on('busy', ({ vhost, mode, queue, size, available, borrowed, min, max }) => {
|
|
561
|
+
if (vhost === 'events') return eventStream.pause();
|
|
562
|
+
console.warn(`vhost ${vhost} is busy`);
|
|
563
|
+
});
|
|
567
564
|
|
|
568
|
-
broker.on(
|
|
569
|
-
|
|
570
|
-
({
|
|
571
|
-
|
|
572
|
-
console.info(`vhost ${vhost} is ready`);
|
|
573
|
-
}
|
|
574
|
-
);
|
|
565
|
+
broker.on('ready', ({ vhost, mode, queue, size, available, borrowed, min, max }) => {
|
|
566
|
+
if (vhost === 'events') return eventStream.resume();
|
|
567
|
+
console.info(`vhost ${vhost} is ready`);
|
|
568
|
+
});
|
|
575
569
|
```
|
|
576
570
|
|
|
577
571
|
#### namespace
|
|
@@ -822,7 +816,7 @@ Now that you've bound your queues and exchanges, you need to start sending them
|
|
|
822
816
|
```
|
|
823
817
|
|
|
824
818
|
```js
|
|
825
|
-
broker.publish(
|
|
819
|
+
broker.publish('p1', 'some message');
|
|
826
820
|
```
|
|
827
821
|
|
|
828
822
|
If you prefer to send messages to a queue
|
|
@@ -862,20 +856,20 @@ Rascal supports text, buffers and anything it can JSON.stringify. Rascal will au
|
|
|
862
856
|
The `broker.publish` method is overloaded to accept a runtime routing key or options.
|
|
863
857
|
|
|
864
858
|
```js
|
|
865
|
-
broker.publish(
|
|
866
|
-
broker.publish(
|
|
867
|
-
broker.publish(
|
|
868
|
-
routingKey:
|
|
869
|
-
options: { messageId:
|
|
859
|
+
broker.publish('p1', 'some message', callback);
|
|
860
|
+
broker.publish('p1', 'some message', 'some.routing.key', callback);
|
|
861
|
+
broker.publish('p1', 'some message', {
|
|
862
|
+
routingKey: 'some.routing.key',
|
|
863
|
+
options: { messageId: 'foo', expiration: 5000 },
|
|
870
864
|
});
|
|
871
865
|
```
|
|
872
866
|
|
|
873
867
|
```js
|
|
874
|
-
await broker.publish(
|
|
875
|
-
await broker.publish(
|
|
876
|
-
await broker.publish(
|
|
877
|
-
routingKey:
|
|
878
|
-
options: { messageId:
|
|
868
|
+
await broker.publish('p1', 'some message');
|
|
869
|
+
await broker.publish('p1', 'some message', 'some.routing.key');
|
|
870
|
+
await broker.publish('p1', 'some message', {
|
|
871
|
+
routingKey: 'some.routing.key',
|
|
872
|
+
options: { messageId: 'foo', expiration: 5000 },
|
|
879
873
|
});
|
|
880
874
|
```
|
|
881
875
|
|
|
@@ -884,33 +878,33 @@ The callback parameters are err (indicating the publication could not be found)
|
|
|
884
878
|
If you specify the "mandatory" option (or use Rascal's defaults) you can also listen for returned messages (i.e. messages that were not delivered to any queues)
|
|
885
879
|
|
|
886
880
|
```js
|
|
887
|
-
broker.publish(
|
|
881
|
+
broker.publish('p1', 'some message', (err, publication) => {
|
|
888
882
|
if (err) throw err; // publication didn't exist
|
|
889
883
|
publication
|
|
890
|
-
.on(
|
|
891
|
-
console.log(
|
|
884
|
+
.on('success', (messageId) => {
|
|
885
|
+
console.log('Message id was: ', messageId);
|
|
892
886
|
})
|
|
893
|
-
.on(
|
|
894
|
-
console.error(
|
|
887
|
+
.on('error', (err, messageId) => {
|
|
888
|
+
console.error('Error was: ', err.message);
|
|
895
889
|
})
|
|
896
|
-
.on(
|
|
897
|
-
console.warn(
|
|
890
|
+
.on('return', (message) => {
|
|
891
|
+
console.warn('Message was returned: ', message.properties.messageId);
|
|
898
892
|
});
|
|
899
893
|
});
|
|
900
894
|
```
|
|
901
895
|
|
|
902
896
|
```js
|
|
903
897
|
try {
|
|
904
|
-
const publication = await broker.publish(
|
|
898
|
+
const publication = await broker.publish('p1', 'some message');
|
|
905
899
|
publication
|
|
906
|
-
.on(
|
|
907
|
-
console.log(
|
|
900
|
+
.on('success', (messageId) => {
|
|
901
|
+
console.log('Message id was: ', messageId);
|
|
908
902
|
})
|
|
909
|
-
.on(
|
|
910
|
-
console.error(
|
|
903
|
+
.on('error', (err, messageId) => {
|
|
904
|
+
console.error('Error was: ', err.message);
|
|
911
905
|
})
|
|
912
|
-
.on(
|
|
913
|
-
console.warn(
|
|
906
|
+
.on('return', (message) => {
|
|
907
|
+
console.warn('Message was returned: ', message.properties.messageId);
|
|
914
908
|
});
|
|
915
909
|
} catch (err) {
|
|
916
910
|
// publication didn't exist
|
|
@@ -971,17 +965,17 @@ If you start experiencing publication timeouts you may find it useful to monitor
|
|
|
971
965
|
Rascal uses a channel pool to publish messages. Access to the channel pool is synchronised via an in memory queue, which will be paused if the connection to the broker is temporarily lost. Consequently instead of erroring, publishes will be held until the connection is re-established. If you would rather abort under these circumstances, you can listen for the publication 'paused' event, and call `publication.abort()`. When the connection is re-established any aborted messages will be dropped instead of published.
|
|
972
966
|
|
|
973
967
|
```js
|
|
974
|
-
broker.publish(
|
|
968
|
+
broker.publish('p1', 'some message', (err, publication) => {
|
|
975
969
|
if (err) throw err; // publication didn't exist
|
|
976
970
|
publication
|
|
977
|
-
.on(
|
|
978
|
-
console.log(
|
|
971
|
+
.on('success', (messageId) => {
|
|
972
|
+
console.log('Message id was: ', messageId);
|
|
979
973
|
})
|
|
980
|
-
.on(
|
|
981
|
-
console.error(
|
|
974
|
+
.on('error', (err, messageId) => {
|
|
975
|
+
console.error('Error was: ', err.message);
|
|
982
976
|
})
|
|
983
|
-
.on(
|
|
984
|
-
console.warn(
|
|
977
|
+
.on('paused', (messageId) => {
|
|
978
|
+
console.warn('Publication was paused. Aborting message: ', messageId);
|
|
985
979
|
publication.abort();
|
|
986
980
|
});
|
|
987
981
|
});
|
|
@@ -989,16 +983,16 @@ broker.publish("p1", "some message", (err, publication) => {
|
|
|
989
983
|
|
|
990
984
|
```js
|
|
991
985
|
try {
|
|
992
|
-
const publication = await broker.publish(
|
|
986
|
+
const publication = await broker.publish('p1', 'some message');
|
|
993
987
|
publication
|
|
994
|
-
.on(
|
|
995
|
-
console.log(
|
|
988
|
+
.on('success', (messageId) => {
|
|
989
|
+
console.log('Message id was: ', messageId);
|
|
996
990
|
})
|
|
997
|
-
.on(
|
|
998
|
-
console.error(
|
|
991
|
+
.on('error', (err, messageId) => {
|
|
992
|
+
console.error('Error was: ', err.message);
|
|
999
993
|
})
|
|
1000
|
-
.on(
|
|
1001
|
-
console.warn(
|
|
994
|
+
.on('paused', (messageId) => {
|
|
995
|
+
console.warn('Publication was paused. Aborting message: ', messageId);
|
|
1002
996
|
publication.abort();
|
|
1003
997
|
});
|
|
1004
998
|
} catch (err) {
|
|
@@ -1042,33 +1036,33 @@ Rascal will set the content type for encrypted messages to 'application/octet-st
|
|
|
1042
1036
|
Sometimes you want to forward a message to a publication. This may be part of a shovel program for transferring messages between vhosts, or because you want to ensure a sequence in some workflow, but do not need to modify the original message. Rascal supports this via `broker.forward`. The syntax is similar to `broker.publish` except from you pass in the original message you want to be forwarded instead of the message payload. If the publication or overrides don't specify a routing key, the original forwarding key will be maintained. The message will also be CC'd with an additional routingkey of `<queue>.<routingKey>` which can be useful for some retry scenarios.
|
|
1043
1037
|
|
|
1044
1038
|
```js
|
|
1045
|
-
broker.forward(
|
|
1039
|
+
broker.forward('p1', message, overrides, (err, publication) => {
|
|
1046
1040
|
if (err) throw err; // publication didn't exist
|
|
1047
1041
|
publication
|
|
1048
|
-
.on(
|
|
1049
|
-
console.log(
|
|
1042
|
+
.on('success', (messageId) => {
|
|
1043
|
+
console.log('Message id was: ', messageId);
|
|
1050
1044
|
})
|
|
1051
|
-
.on(
|
|
1052
|
-
console.error(
|
|
1045
|
+
.on('error', (err, messageId) => {
|
|
1046
|
+
console.error('Error was: ', err.message);
|
|
1053
1047
|
})
|
|
1054
|
-
.on(
|
|
1055
|
-
console.warn(
|
|
1048
|
+
.on('return', (message) => {
|
|
1049
|
+
console.warn('Message was returned: ', message.properties.messageId);
|
|
1056
1050
|
});
|
|
1057
1051
|
});
|
|
1058
1052
|
```
|
|
1059
1053
|
|
|
1060
1054
|
```js
|
|
1061
1055
|
try {
|
|
1062
|
-
const publication = await broker.forward(
|
|
1056
|
+
const publication = await broker.forward('p1', message, overrides);
|
|
1063
1057
|
publication
|
|
1064
|
-
.on(
|
|
1065
|
-
console.log(
|
|
1058
|
+
.on('success', (messageId) => {
|
|
1059
|
+
console.log('Message id was: ', messageId);
|
|
1066
1060
|
})
|
|
1067
|
-
.on(
|
|
1068
|
-
console.error(
|
|
1061
|
+
.on('error', (err, messageId) => {
|
|
1062
|
+
console.error('Error was: ', err.message);
|
|
1069
1063
|
})
|
|
1070
|
-
.on(
|
|
1071
|
-
console.warn(
|
|
1064
|
+
.on('return', (message) => {
|
|
1065
|
+
console.warn('Message was returned: ', message.properties.messageId);
|
|
1072
1066
|
});
|
|
1073
1067
|
} catch (err) {
|
|
1074
1068
|
// publication didn't exist
|
|
@@ -1095,27 +1089,27 @@ The real fun begins with subscriptions
|
|
|
1095
1089
|
```
|
|
1096
1090
|
|
|
1097
1091
|
```js
|
|
1098
|
-
broker.subscribe(
|
|
1092
|
+
broker.subscribe('s1', (err, subscription) => {
|
|
1099
1093
|
if (err) throw err; // subscription didn't exist
|
|
1100
1094
|
subscription
|
|
1101
|
-
.on(
|
|
1095
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1102
1096
|
// Do stuff with message
|
|
1103
1097
|
})
|
|
1104
|
-
.on(
|
|
1105
|
-
console.error(
|
|
1098
|
+
.on('error', (err) => {
|
|
1099
|
+
console.error('Subscriber error', err);
|
|
1106
1100
|
});
|
|
1107
1101
|
});
|
|
1108
1102
|
```
|
|
1109
1103
|
|
|
1110
1104
|
```js
|
|
1111
1105
|
try {
|
|
1112
|
-
const subscription = await broker.subscribe(
|
|
1106
|
+
const subscription = await broker.subscribe('s1');
|
|
1113
1107
|
subscription
|
|
1114
|
-
.on(
|
|
1108
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1115
1109
|
// Do stuff with message
|
|
1116
1110
|
})
|
|
1117
|
-
.on(
|
|
1118
|
-
console.error(
|
|
1111
|
+
.on('error', (err) => {
|
|
1112
|
+
console.error('Subscriber error', err);
|
|
1119
1113
|
});
|
|
1120
1114
|
} catch (err) {
|
|
1121
1115
|
// subscription didn't exist
|
|
@@ -1143,7 +1137,7 @@ Rascal supports text, buffers and anything it can JSON.parse, providing the cont
|
|
|
1143
1137
|
The `broker.subscribe` method also accepts an options parameter which will override options specified in config
|
|
1144
1138
|
|
|
1145
1139
|
```js
|
|
1146
|
-
broker.subscribe(
|
|
1140
|
+
broker.subscribe('s1', { prefetch: 10, retry: false }, callback);
|
|
1147
1141
|
```
|
|
1148
1142
|
|
|
1149
1143
|
```js
|
|
@@ -1163,11 +1157,11 @@ broker.subscribeAll((err, subscriptions) => {
|
|
|
1163
1157
|
if (err) throw err; // one or more subscriptions didn't exist
|
|
1164
1158
|
subscriptions.forEach((subscription) => {
|
|
1165
1159
|
subscription
|
|
1166
|
-
.on(
|
|
1160
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1167
1161
|
// Do stuff with message
|
|
1168
1162
|
})
|
|
1169
|
-
.on(
|
|
1170
|
-
console.error(
|
|
1163
|
+
.on('error', (err) => {
|
|
1164
|
+
console.error('Subscriber error', err);
|
|
1171
1165
|
});
|
|
1172
1166
|
});
|
|
1173
1167
|
});
|
|
@@ -1178,11 +1172,11 @@ try {
|
|
|
1178
1172
|
const subscriptions = await broker.subscribeAll();
|
|
1179
1173
|
subscriptions.forEach((subscription) => {
|
|
1180
1174
|
subscription
|
|
1181
|
-
.on(
|
|
1175
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1182
1176
|
// Do stuff with message
|
|
1183
1177
|
})
|
|
1184
|
-
.on(
|
|
1185
|
-
console.error(
|
|
1178
|
+
.on('error', (err) => {
|
|
1179
|
+
console.error('Subscriber error', err);
|
|
1186
1180
|
});
|
|
1187
1181
|
});
|
|
1188
1182
|
} catch (err) {
|
|
@@ -1199,11 +1193,11 @@ broker.subscribeAll(
|
|
|
1199
1193
|
if (err) throw err; // one or more subscriptions didn't exist
|
|
1200
1194
|
subscriptions.forEach((subscription) => {
|
|
1201
1195
|
subscription
|
|
1202
|
-
.on(
|
|
1196
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1203
1197
|
// Do stuff with message
|
|
1204
1198
|
})
|
|
1205
|
-
.on(
|
|
1206
|
-
console.error(
|
|
1199
|
+
.on('error', (err) => {
|
|
1200
|
+
console.error('Subscriber error', err);
|
|
1207
1201
|
});
|
|
1208
1202
|
});
|
|
1209
1203
|
}
|
|
@@ -1230,17 +1224,17 @@ try {
|
|
|
1230
1224
|
If rascal can't parse the content (e.g. the message had a content type of 'application/json' but the content was not JSON), it will emit an 'invalid_content' event
|
|
1231
1225
|
|
|
1232
1226
|
```js
|
|
1233
|
-
broker.subscribe(
|
|
1227
|
+
broker.subscribe('s1', (err, subscription) => {
|
|
1234
1228
|
if (err) throw err; // subscription didn't exist
|
|
1235
1229
|
subscription
|
|
1236
|
-
.on(
|
|
1230
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1237
1231
|
// Do stuff with message
|
|
1238
1232
|
})
|
|
1239
|
-
.on(
|
|
1240
|
-
console.error(
|
|
1233
|
+
.on('error', (err) => {
|
|
1234
|
+
console.error('Subscriber error', err);
|
|
1241
1235
|
})
|
|
1242
|
-
.on(
|
|
1243
|
-
console.error(
|
|
1236
|
+
.on('invalid_content', (err, message, ackOrNack) => {
|
|
1237
|
+
console.error('Invalid content', err);
|
|
1244
1238
|
ackOrNack(err);
|
|
1245
1239
|
});
|
|
1246
1240
|
});
|
|
@@ -1248,16 +1242,16 @@ broker.subscribe("s1", (err, subscription) => {
|
|
|
1248
1242
|
|
|
1249
1243
|
```js
|
|
1250
1244
|
try {
|
|
1251
|
-
const subscription = await broker.subscribe(
|
|
1245
|
+
const subscription = await broker.subscribe('s1');
|
|
1252
1246
|
subscription
|
|
1253
|
-
.on(
|
|
1247
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1254
1248
|
// Do stuff with message
|
|
1255
1249
|
})
|
|
1256
|
-
.on(
|
|
1257
|
-
console.error(
|
|
1250
|
+
.on('error', (err) => {
|
|
1251
|
+
console.error('Subscriber error', err);
|
|
1258
1252
|
})
|
|
1259
|
-
.on(
|
|
1260
|
-
console.error(
|
|
1253
|
+
.on('invalid_content', (err, message, ackOrNack) => {
|
|
1254
|
+
console.error('Invalid content', err);
|
|
1261
1255
|
ackOrNack(err);
|
|
1262
1256
|
});
|
|
1263
1257
|
} catch (err) {
|
|
@@ -1330,17 +1324,17 @@ If your app crashes before acknowledging a message, the message will be rolled b
|
|
|
1330
1324
|
```
|
|
1331
1325
|
|
|
1332
1326
|
```js
|
|
1333
|
-
broker.subscribe(
|
|
1327
|
+
broker.subscribe('s1', (err, subscription) => {
|
|
1334
1328
|
if (err) throw err; // subscription didn't exist
|
|
1335
1329
|
subscription
|
|
1336
|
-
.on(
|
|
1330
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1337
1331
|
// Do stuff with message
|
|
1338
1332
|
})
|
|
1339
|
-
.on(
|
|
1340
|
-
console.error(
|
|
1333
|
+
.on('error', (err) => {
|
|
1334
|
+
console.error('Subscriber error', err);
|
|
1341
1335
|
})
|
|
1342
|
-
.on(
|
|
1343
|
-
console.error(
|
|
1336
|
+
.on('redeliveries_exceeded', (err, message, ackOrNack) => {
|
|
1337
|
+
console.error('Redeliveries exceeded', err);
|
|
1344
1338
|
ackOrNack(err);
|
|
1345
1339
|
});
|
|
1346
1340
|
});
|
|
@@ -1348,16 +1342,16 @@ broker.subscribe("s1", (err, subscription) => {
|
|
|
1348
1342
|
|
|
1349
1343
|
```js
|
|
1350
1344
|
try {
|
|
1351
|
-
const subscription = await broker.subscribe(
|
|
1345
|
+
const subscription = await broker.subscribe('s1');
|
|
1352
1346
|
subscription
|
|
1353
|
-
.on(
|
|
1347
|
+
.on('message', (message, content, ackOrNack) => {
|
|
1354
1348
|
// Do stuff with message
|
|
1355
1349
|
})
|
|
1356
|
-
.on(
|
|
1357
|
-
console.error(
|
|
1350
|
+
.on('error', (err) => {
|
|
1351
|
+
console.error('Subscriber error', err);
|
|
1358
1352
|
})
|
|
1359
|
-
.on(
|
|
1360
|
-
console.error(
|
|
1353
|
+
.on('redeliveries_exceeded', (err, message, ackOrNack) => {
|
|
1354
|
+
console.error('Redeliveries exceeded', err);
|
|
1361
1355
|
ackOrNack(err);
|
|
1362
1356
|
});
|
|
1363
1357
|
} catch (err) {
|
|
@@ -1392,7 +1386,7 @@ When using the promises API, ackOrNack will work as for the callback API unless
|
|
|
1392
1386
|
##### Nack (Reject or Dead Letter)
|
|
1393
1387
|
|
|
1394
1388
|
```js
|
|
1395
|
-
ackOrNack(err, { strategy:
|
|
1389
|
+
ackOrNack(err, { strategy: 'nack' });
|
|
1396
1390
|
```
|
|
1397
1391
|
|
|
1398
1392
|
Nack causes the message to be discarded or routed to a dead letter exchange if configured.
|
|
@@ -1400,7 +1394,7 @@ Nack causes the message to be discarded or routed to a dead letter exchange if c
|
|
|
1400
1394
|
##### Nack with Requeue
|
|
1401
1395
|
|
|
1402
1396
|
```js
|
|
1403
|
-
ackOrNack(err, { strategy:
|
|
1397
|
+
ackOrNack(err, { strategy: 'nack', defer: 1000, requeue: true });
|
|
1404
1398
|
```
|
|
1405
1399
|
|
|
1406
1400
|
The defer option is not mandatory, but without it you are likely retry your message thousands of times a second. Even then requeueing is a inadequate strategy for error handling, since the message will be rolled back to the front of the queue and there is no simple way to detect how many times the message has been redelivered.
|
|
@@ -1410,7 +1404,7 @@ Dead lettering is a good option for invalid messages but with one major flaw - b
|
|
|
1410
1404
|
##### Republish
|
|
1411
1405
|
|
|
1412
1406
|
```js
|
|
1413
|
-
ackOrNack(err, { strategy:
|
|
1407
|
+
ackOrNack(err, { strategy: 'republish', defer: 1000 });
|
|
1414
1408
|
```
|
|
1415
1409
|
|
|
1416
1410
|
An alternative to nacking to republish the message back to the queue it came from. This has the advantage that the message will be resent to the back of the queue, allowing other messages to be processed and potentially fixing errors relating to ordering.
|
|
@@ -1418,10 +1412,7 @@ An alternative to nacking to republish the message back to the queue it came fro
|
|
|
1418
1412
|
Rascal keeps track of the number of republishes so you can limit the number of attempts. **Whenever you specify a number of attempts you should always chain a fallback strategy**, otherwise if the attempts are exceeded your message will be neither acked or nacked.
|
|
1419
1413
|
|
|
1420
1414
|
```js
|
|
1421
|
-
ackOrNack(err, [
|
|
1422
|
-
{ strategy: "republish", defer: 1000, attempts: 10 },
|
|
1423
|
-
{ strategy: "nack" },
|
|
1424
|
-
]);
|
|
1415
|
+
ackOrNack(err, [{ strategy: 'republish', defer: 1000, attempts: 10 }, { strategy: 'nack' }]);
|
|
1425
1416
|
```
|
|
1426
1417
|
|
|
1427
1418
|
Rascal also annotates the message with detail of the error `message.properties.headers.rascal.<queue>.error` which can be useful if you eventually dead letter it.
|
|
@@ -1441,7 +1432,7 @@ Before using republish please consider the following:
|
|
|
1441
1432
|
As mentioned previously, dead lettering invalid messages is a good strategy with one flaw - since there is no way to modify the message you cannot annotate it with failure details. A solution to this is to republish with attempts = 1 and then nacking it to a dead letter exchange. The problem with this approach is that invalid messages will always be processed twice. To workaround this set immediateNack to true in the recovery options. This will instruct Rascal to nack the message immediately instead of emitting the 'message' event.
|
|
1442
1433
|
|
|
1443
1434
|
```js
|
|
1444
|
-
ackOrNack(err, { strategy:
|
|
1435
|
+
ackOrNack(err, { strategy: 'republish', immediateNack: true });
|
|
1445
1436
|
```
|
|
1446
1437
|
|
|
1447
1438
|
If you ever want to resend the message to the same queue you will have to remove the `properties.headers.rascal.<queue>.immediateNack` header first.
|
|
@@ -1451,7 +1442,7 @@ If you ever want to resend the message to the same queue you will have to remove
|
|
|
1451
1442
|
Instead of republishing the message to the same queue you can forward it to a Rascal publication. You should read the section entitled [Forwarding messages](#Forwarding-messages) to understand the risks of this.
|
|
1452
1443
|
|
|
1453
1444
|
```js
|
|
1454
|
-
ackOrNack(err, { strategy:
|
|
1445
|
+
ackOrNack(err, { strategy: 'forward', publication: 'some_exchange' });
|
|
1455
1446
|
```
|
|
1456
1447
|
|
|
1457
1448
|
**Danger**
|
|
@@ -1462,12 +1453,12 @@ Furthermore if the message is forwarded but cannot be routed (e.g. due to an inc
|
|
|
1462
1453
|
```js
|
|
1463
1454
|
ackOrNack(err, [
|
|
1464
1455
|
{
|
|
1465
|
-
strategy:
|
|
1466
|
-
publication:
|
|
1456
|
+
strategy: 'forward',
|
|
1457
|
+
publication: 'some_exchange',
|
|
1467
1458
|
defer: 1000,
|
|
1468
1459
|
attempts: 10,
|
|
1469
1460
|
},
|
|
1470
|
-
{ strategy:
|
|
1461
|
+
{ strategy: 'nack' },
|
|
1471
1462
|
]);
|
|
1472
1463
|
```
|
|
1473
1464
|
|
|
@@ -1476,11 +1467,11 @@ You can also override the publication options
|
|
|
1476
1467
|
```js
|
|
1477
1468
|
ackOrNack(err, [
|
|
1478
1469
|
{
|
|
1479
|
-
strategy:
|
|
1480
|
-
publication:
|
|
1481
|
-
options: { routingKey:
|
|
1470
|
+
strategy: 'forward',
|
|
1471
|
+
publication: 'some_exchange',
|
|
1472
|
+
options: { routingKey: 'custom.routing.key' },
|
|
1482
1473
|
},
|
|
1483
|
-
{ strategy:
|
|
1474
|
+
{ strategy: 'nack' },
|
|
1484
1475
|
]);
|
|
1485
1476
|
```
|
|
1486
1477
|
|
|
@@ -1491,7 +1482,7 @@ One use of the forward recovery strategy is to send messages to a wait queue whi
|
|
|
1491
1482
|
Acknowledges the message, guaranteeing that it will be discarded in the event you also have a dead letter exchange configured. Sometimes useful in automated tests or when chaining a sequence of other recovery strategies.
|
|
1492
1483
|
|
|
1493
1484
|
```js
|
|
1494
|
-
ackOrNack(err, { strategy:
|
|
1485
|
+
ackOrNack(err, { strategy: 'ack' });
|
|
1495
1486
|
```
|
|
1496
1487
|
|
|
1497
1488
|
#### Chaining Recovery Strategies
|
|
@@ -1501,18 +1492,18 @@ By chaining Rascal's recovery strategies and leveraging some of RabbitMQs lesser
|
|
|
1501
1492
|
```js
|
|
1502
1493
|
ackOrNack(err, [
|
|
1503
1494
|
{
|
|
1504
|
-
strategy:
|
|
1495
|
+
strategy: 'republish',
|
|
1505
1496
|
defer: 1000,
|
|
1506
1497
|
attempts: 10,
|
|
1507
1498
|
},
|
|
1508
1499
|
{
|
|
1509
|
-
strategy:
|
|
1500
|
+
strategy: 'nack',
|
|
1510
1501
|
},
|
|
1511
1502
|
]);
|
|
1512
1503
|
```
|
|
1513
1504
|
|
|
1514
1505
|
Far more sophisticated strategies are achievable...
|
|
1515
|
-

|
|
1516
1507
|
|
|
1517
1508
|
1. Producer publishes a message with the routing key "a.b.c" to the "jobs" topic exchange
|
|
1518
1509
|
2. The message is routed to the "incoming" queue. The "incoming" queue is configured with a dead letter exchange.
|
|
@@ -1537,24 +1528,24 @@ If an error occurs on the channel (which will happen if you accidentally acknowl
|
|
|
1537
1528
|
|
|
1538
1529
|
```js
|
|
1539
1530
|
// Does not retry. This will cause an error to be emitted which unhandled will crash your process. See [Subscriber Events](#subscriber-events)
|
|
1540
|
-
broker.subscribe(
|
|
1531
|
+
broker.subscribe('s1', { prefetch: 10, retry: false }, callback);
|
|
1541
1532
|
|
|
1542
1533
|
// Retries without delay.
|
|
1543
|
-
broker.subscribe(
|
|
1534
|
+
broker.subscribe('s1', { prefetch: 10, retry: true }, callback);
|
|
1544
1535
|
|
|
1545
1536
|
// Retries after a one second interval.
|
|
1546
|
-
broker.subscribe(
|
|
1537
|
+
broker.subscribe('s1', { prefetch: 10, retry: { delay: 1000 } }, callback);
|
|
1547
1538
|
```
|
|
1548
1539
|
|
|
1549
1540
|
```js
|
|
1550
1541
|
// Does not retry. This will cause an error to be emitted which unhandled will crash your process. See [Subscriber Events](#subscriber-events)
|
|
1551
|
-
await broker.subscribe(
|
|
1542
|
+
await broker.subscribe('s1', { prefetch: 10, retry: false });
|
|
1552
1543
|
|
|
1553
1544
|
// Retries without delay.
|
|
1554
|
-
await broker.subscribe(
|
|
1545
|
+
await broker.subscribe('s1', { prefetch: 10, retry: true });
|
|
1555
1546
|
|
|
1556
1547
|
// Retries after a one second interval.
|
|
1557
|
-
await broker.subscribe(
|
|
1548
|
+
await broker.subscribe('s1', { prefetch: 10, retry: { delay: 1000 } });
|
|
1558
1549
|
```
|
|
1559
1550
|
|
|
1560
1551
|
#### Subscriber Events
|
|
@@ -1610,7 +1601,7 @@ Configuring each vhost, exchange, queue, binding, publication and subscription e
|
|
|
1610
1601
|
You can cancel subscriptions as follows
|
|
1611
1602
|
|
|
1612
1603
|
```js
|
|
1613
|
-
broker.subscribe(
|
|
1604
|
+
broker.subscribe('s1', (err, subscription) => {
|
|
1614
1605
|
if (err) throw err; // subscription didn't exist
|
|
1615
1606
|
subscription.cancel((err) => {
|
|
1616
1607
|
console.err(err);
|
|
@@ -1620,18 +1611,18 @@ broker.subscribe("s1", (err, subscription) => {
|
|
|
1620
1611
|
|
|
1621
1612
|
```js
|
|
1622
1613
|
try {
|
|
1623
|
-
const subscription = await broker.subscribe(
|
|
1614
|
+
const subscription = await broker.subscribe('s1');
|
|
1624
1615
|
await subscription.cancel();
|
|
1625
1616
|
} catch (err) {
|
|
1626
1617
|
// subscription didn't exist or could not be cancelled
|
|
1627
1618
|
}
|
|
1628
1619
|
```
|
|
1629
1620
|
|
|
1630
|
-
Cancelling a subscribion will stop consuming messages, but leave the channel open
|
|
1621
|
+
Cancelling a subscribion will stop consuming messages, but leave the channel open until any outstanding messages have been acknowledged, or the timeout specified by through the `closeTimeout` subscription property is exceeded.
|
|
1631
1622
|
|
|
1632
1623
|
## Shutdown
|
|
1633
1624
|
|
|
1634
|
-
You can shutdown the broker by calling `await broker.shutdown()` or `broker.shutdown(cb)`.
|
|
1625
|
+
You can shutdown the broker by calling `await broker.shutdown()` or `broker.shutdown(cb)`.
|
|
1635
1626
|
|
|
1636
1627
|
## Bonus Features
|
|
1637
1628
|
|
|
@@ -1705,7 +1696,7 @@ is equivalent to...
|
|
|
1705
1696
|
Rascal is a rich pub/sub wrapper and as such hides much of the amqplib [channel api](https://www.squaremobius.net/amqp.node/channel_api.html#channel). If you need to access this you can programmatically establish a connection to a vhost as follows.
|
|
1706
1697
|
|
|
1707
1698
|
```js
|
|
1708
|
-
broker.connect(
|
|
1699
|
+
broker.connect('/', (err, connection) => {
|
|
1709
1700
|
if (err) throw new Error(`Connection error: ${err.message}`);
|
|
1710
1701
|
// profit
|
|
1711
1702
|
});
|
|
@@ -1713,7 +1704,7 @@ broker.connect("/", (err, connection) => {
|
|
|
1713
1704
|
|
|
1714
1705
|
```js
|
|
1715
1706
|
try {
|
|
1716
|
-
const connection = broker.connect(
|
|
1707
|
+
const connection = broker.connect('/');
|
|
1717
1708
|
// profit
|
|
1718
1709
|
} catch (err) {
|
|
1719
1710
|
throw new Error(`Connection error: ${err.message}`);
|