@usermetrics/queuebit 1.0.0 → 1.0.5
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/README.md +91 -198
- package/docs/API.md +116 -376
- package/docs/EXAMPLES.md +94 -225
- package/docs/QUICKSTART.md +64 -65
- package/examples/inprocessserver.js +60 -0
- package/examples/loadbalancer.js +56 -0
- package/examples/{browser-example.html → qpanel.html} +18 -11
- package/examples/server2server.js +56 -0
- package/examples/start_load_balancer.cmd +1 -0
- package/examples/start_node_client.cmd +1 -0
- package/examples/start_node_inprocess.cmd +1 -0
- package/package.json +6 -6
- package/src/client-browser.js +12 -6
- package/src/client-node.js +14 -8
- package/src/client.js +0 -3
- package/src/server.js +64 -52
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load Balancer demo - load balanced message delivery.
|
|
3
|
+
* Messages are distributed round-robin across workers in the same load balancer.
|
|
4
|
+
* Only ONE worker receives each message (unlike regular subscribe where ALL receive).
|
|
5
|
+
*
|
|
6
|
+
* Run this example with: node examples/queuegroup.js
|
|
7
|
+
*/
|
|
8
|
+
const { QueueBitServer } = require('../src/server');
|
|
9
|
+
const { QueueBitClient } = require('../src/client-node');
|
|
10
|
+
|
|
11
|
+
const PORT = 3333;
|
|
12
|
+
|
|
13
|
+
// Start QueueBit server in-process
|
|
14
|
+
new QueueBitServer({ port: PORT });
|
|
15
|
+
|
|
16
|
+
async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
await sleep(500); // wait for server to start
|
|
20
|
+
|
|
21
|
+
// Create 3 worker clients - all in the same load balancer 'workers' on subject 'jobs'
|
|
22
|
+
const worker1 = new QueueBitClient(`http://localhost:${PORT}`);
|
|
23
|
+
const worker2 = new QueueBitClient(`http://localhost:${PORT}`);
|
|
24
|
+
const worker3 = new QueueBitClient(`http://localhost:${PORT}`);
|
|
25
|
+
|
|
26
|
+
await sleep(500); // wait for clients to connect
|
|
27
|
+
|
|
28
|
+
await worker1.subscribe((msg) => {
|
|
29
|
+
console.log(`Worker 1 received:`, msg.data);
|
|
30
|
+
}, { subject: 'jobs', queue: 'workers' });
|
|
31
|
+
|
|
32
|
+
await worker2.subscribe((msg) => {
|
|
33
|
+
console.log(`Worker 2 received:`, msg.data);
|
|
34
|
+
}, { subject: 'jobs', queue: 'workers' });
|
|
35
|
+
|
|
36
|
+
await worker3.subscribe((msg) => {
|
|
37
|
+
console.log(`Worker 3 received:`, msg.data);
|
|
38
|
+
}, { subject: 'jobs', queue: 'workers' });
|
|
39
|
+
|
|
40
|
+
// Publisher client
|
|
41
|
+
const publisher = new QueueBitClient(`http://localhost:${PORT}`);
|
|
42
|
+
await sleep(500);
|
|
43
|
+
|
|
44
|
+
console.log('\nPublishing 6 jobs - each worker should receive 2 (round-robin):\n');
|
|
45
|
+
|
|
46
|
+
for (let i = 1; i <= 6; i++) {
|
|
47
|
+
await publisher.publish({ job: `task-${i}`, payload: `data-${i}` }, { subject: 'jobs' });
|
|
48
|
+
await sleep(100);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await sleep(500);
|
|
52
|
+
console.log('\nDone. Each worker received ~2 messages.');
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main();
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
<h2>Subscribe</h2>
|
|
125
125
|
<input type="text" id="subscribeSubject" placeholder="Subject" class="small">
|
|
126
126
|
<button onclick="subscribe()">Subscribe</button>
|
|
127
|
-
<button onclick="subscribeQueue()">
|
|
127
|
+
<button onclick="subscribeQueue()">Add Load Balancer</button>
|
|
128
128
|
</div>
|
|
129
129
|
|
|
130
130
|
<div class="col container">
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
let lastUpdateTime = 0;
|
|
163
163
|
let publishEndTime = 0;
|
|
164
164
|
let isConnected = false;
|
|
165
|
+
let lbSubscriptionCount = 0; // track how many LB subscriptions added
|
|
165
166
|
|
|
166
167
|
function connect() {
|
|
167
168
|
const url = document.getElementById('serverUrl').value;
|
|
@@ -194,6 +195,7 @@
|
|
|
194
195
|
client.disconnect();
|
|
195
196
|
client = null;
|
|
196
197
|
isConnected = false;
|
|
198
|
+
lbSubscriptionCount = 0;
|
|
197
199
|
document.getElementById('status').textContent = 'Disconnected';
|
|
198
200
|
document.getElementById('status').style.color = 'black';
|
|
199
201
|
document.getElementById('serverVersion').textContent = '';
|
|
@@ -270,14 +272,25 @@
|
|
|
270
272
|
}
|
|
271
273
|
|
|
272
274
|
const subject = document.getElementById('subscribeSubject').value || 'default';
|
|
275
|
+
lbSubscriptionCount++;
|
|
276
|
+
const workerNum = lbSubscriptionCount;
|
|
277
|
+
const uniqueQueueName = `browser-worker-${workerNum}`; // unique name per worker
|
|
273
278
|
|
|
274
|
-
await client.subscribe((message) => {
|
|
279
|
+
const response = await client.subscribe((message) => {
|
|
275
280
|
if (!perfTestRunning) {
|
|
276
|
-
|
|
281
|
+
console.log('LB message received:', JSON.stringify({
|
|
282
|
+
queueName: message.queueName,
|
|
283
|
+
loadBalancerId: message.loadBalancerId,
|
|
284
|
+
subject: message.subject
|
|
285
|
+
}));
|
|
286
|
+
const lbId = message.loadBalancerId !== undefined ? ` LB#${message.loadBalancerId}` : '';
|
|
287
|
+
addMessage(`[Worker #${workerNum}${lbId}: ${subject}] ${JSON.stringify(message.data)}`);
|
|
277
288
|
}
|
|
278
|
-
}, { subject, queue:
|
|
289
|
+
}, { subject, queue: uniqueQueueName });
|
|
279
290
|
|
|
280
|
-
|
|
291
|
+
const lbId = response?.loadBalancerId !== undefined ? ` (LB#${response.loadBalancerId})` : '';
|
|
292
|
+
addMessage(`✓ Load Balancer Worker #${workerNum}${lbId} added for subject "${subject}"`);
|
|
293
|
+
console.log(`Worker #${workerNum} subscribed to load balancer: ${subject} (queue: ${uniqueQueueName})`);
|
|
281
294
|
}
|
|
282
295
|
|
|
283
296
|
function addMessage(text, isPerf = false) {
|
|
@@ -350,12 +363,6 @@
|
|
|
350
363
|
const totalMessages = parseInt(document.getElementById('testMessageCount').value);
|
|
351
364
|
console.log('totalMessages:', totalMessages);
|
|
352
365
|
|
|
353
|
-
const confirmed = confirm(`This will send ${totalMessages.toLocaleString()} messages to the queue. Continue?`);
|
|
354
|
-
if (!confirmed) {
|
|
355
|
-
console.log('Test cancelled by user');
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
366
|
console.log('Starting performance test...');
|
|
360
367
|
perfTestRunning = true;
|
|
361
368
|
receivedCount = 0;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server to server queue.
|
|
3
|
+
* This adds queue functionality to a simple http server (the queue server is running elsewhere)
|
|
4
|
+
* Use the qpanel.html dashboard to publish messages to the queue and see them received by the HTTP server.
|
|
5
|
+
* run the server with /start_server.cmd and then run this example with /examples/start_node_client.cmd
|
|
6
|
+
*/
|
|
7
|
+
const http = require('http');
|
|
8
|
+
const { QueueBitClient } = require('../src/client-node');
|
|
9
|
+
|
|
10
|
+
const webserverPORT = 3000;
|
|
11
|
+
const queuebitPORT = 3333;
|
|
12
|
+
|
|
13
|
+
// Initialize a queuebit message queue. It will connect to the server running on port 3333
|
|
14
|
+
const messageQueue = new QueueBitClient(`http://localhost:${queuebitPORT}`);
|
|
15
|
+
|
|
16
|
+
// Create an HTTP server
|
|
17
|
+
const server = http.createServer((req, res) => {
|
|
18
|
+
res.setHeader('Content-Type', 'application/json');
|
|
19
|
+
|
|
20
|
+
if (req.method === 'POST' && req.url === '/enqueue') {
|
|
21
|
+
let body = '';
|
|
22
|
+
req.on('data', chunk => {
|
|
23
|
+
body += chunk.toString();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
req.on('end', () => {
|
|
27
|
+
try {
|
|
28
|
+
const message = JSON.parse(body);
|
|
29
|
+
messageQueue.enqueue(message);
|
|
30
|
+
res.writeHead(200);
|
|
31
|
+
res.end(JSON.stringify({ success: true, message: 'Message enqueued' }));
|
|
32
|
+
} catch (error) {
|
|
33
|
+
res.writeHead(400);
|
|
34
|
+
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
} else if (req.method === 'GET' && req.url === '/dequeue') {
|
|
38
|
+
const message = messageQueue.dequeue();
|
|
39
|
+
res.writeHead(200);
|
|
40
|
+
res.end(JSON.stringify({ message: message || 'Queue is empty' }));
|
|
41
|
+
} else if (req.method === 'GET' && req.url === '/size') {
|
|
42
|
+
res.writeHead(200);
|
|
43
|
+
res.end(JSON.stringify({ size: messageQueue.size() }));
|
|
44
|
+
} else {
|
|
45
|
+
res.writeHead(404);
|
|
46
|
+
res.end(JSON.stringify({ error: 'Not found' }));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
server.listen(webserverPORT, () => {
|
|
51
|
+
console.log(`Server running at http://localhost:${webserverPORT}`);
|
|
52
|
+
console.log('Endpoints:');
|
|
53
|
+
console.log(' POST /enqueue - Add message to queue');
|
|
54
|
+
console.log(' GET /dequeue - Remove and return message from queue');
|
|
55
|
+
console.log(' GET /size - Get current queue size');
|
|
56
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
node ./loadbalancer.js
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
node ./server2server.js
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
call node inprocessserver.js
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usermetrics/queuebit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"description": "A
|
|
7
|
+
"description": "A high performance queue with guaranteed delivery and built-in load balancer. Runs in-process or as a standalone server. Supports browser clients.",
|
|
8
8
|
"main": "src/index.js",
|
|
9
9
|
"browser": "src/client-browser.js",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "node test/test-harness.js",
|
|
12
|
-
"start": "node src/
|
|
12
|
+
"start": "node src/server-runner.js",
|
|
13
13
|
"server": "node src/server-runner.js",
|
|
14
|
+
"auto": "nodemon src/server-runner.js",
|
|
14
15
|
"prepublishOnly": "npm test"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
@@ -26,11 +27,10 @@
|
|
|
26
27
|
"license": "MIT",
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"socket.io": "^4.7.2",
|
|
30
|
+
"socket.io-client": "^4.7.2",
|
|
29
31
|
"uuid": "^9.0.1"
|
|
30
32
|
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"socket.io-client": "^4.7.2"
|
|
33
|
-
},
|
|
33
|
+
"devDependencies": {},
|
|
34
34
|
"files": [
|
|
35
35
|
"src/",
|
|
36
36
|
"docs/",
|
package/src/client-browser.js
CHANGED
|
@@ -79,12 +79,14 @@ class QueueBitClient {
|
|
|
79
79
|
|
|
80
80
|
subscribe(callback, options = {}) {
|
|
81
81
|
const subject = options.subject || 'default';
|
|
82
|
+
const queueName = options.queue || null;
|
|
83
|
+
const handlerKey = queueName ? `${subject}:${queueName}` : subject;
|
|
82
84
|
|
|
83
|
-
if (!this.messageHandlers.has(
|
|
84
|
-
this.messageHandlers.set(
|
|
85
|
+
if (!this.messageHandlers.has(handlerKey)) {
|
|
86
|
+
this.messageHandlers.set(handlerKey, new Set());
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
this.messageHandlers.get(
|
|
89
|
+
this.messageHandlers.get(handlerKey).add(callback);
|
|
88
90
|
|
|
89
91
|
return new Promise((resolve) => {
|
|
90
92
|
this.socket.emit('subscribe', options, (response) => {
|
|
@@ -95,7 +97,9 @@ class QueueBitClient {
|
|
|
95
97
|
|
|
96
98
|
unsubscribe(options = {}) {
|
|
97
99
|
const subject = options.subject || 'default';
|
|
98
|
-
|
|
100
|
+
const queueName = options.queue || null;
|
|
101
|
+
const handlerKey = queueName ? `${subject}:${queueName}` : subject;
|
|
102
|
+
this.messageHandlers.delete(handlerKey);
|
|
99
103
|
|
|
100
104
|
return new Promise((resolve) => {
|
|
101
105
|
this.socket.emit('unsubscribe', options, (response) => {
|
|
@@ -114,8 +118,10 @@ class QueueBitClient {
|
|
|
114
118
|
|
|
115
119
|
handleMessage(message) {
|
|
116
120
|
const subject = message.subject || 'default';
|
|
117
|
-
const
|
|
118
|
-
|
|
121
|
+
const queueName = message.queueName || null;
|
|
122
|
+
const handlerKey = queueName ? `${subject}:${queueName}` : subject;
|
|
123
|
+
|
|
124
|
+
const handlers = this.messageHandlers.get(handlerKey);
|
|
119
125
|
if (handlers) {
|
|
120
126
|
for (const handler of handlers) {
|
|
121
127
|
handler(message);
|
package/src/client-node.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { io } = require('socket.io-client');
|
|
2
2
|
|
|
3
3
|
class QueueBitClient {
|
|
4
|
-
constructor(url = 'http://localhost:
|
|
4
|
+
constructor(url = 'http://localhost:3333') {
|
|
5
5
|
this.socket = io(url, {
|
|
6
6
|
transports: ['websocket'],
|
|
7
7
|
upgrade: false,
|
|
@@ -60,12 +60,14 @@ class QueueBitClient {
|
|
|
60
60
|
|
|
61
61
|
subscribe(callback, options = {}) {
|
|
62
62
|
const subject = options.subject || 'default';
|
|
63
|
+
const queueName = options.queue || null;
|
|
64
|
+
const handlerKey = queueName ? `${subject}:${queueName}` : subject;
|
|
63
65
|
|
|
64
|
-
if (!this.messageHandlers.has(
|
|
65
|
-
this.messageHandlers.set(
|
|
66
|
+
if (!this.messageHandlers.has(handlerKey)) {
|
|
67
|
+
this.messageHandlers.set(handlerKey, new Set());
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
this.messageHandlers.get(
|
|
70
|
+
this.messageHandlers.get(handlerKey).add(callback);
|
|
69
71
|
|
|
70
72
|
return new Promise((resolve) => {
|
|
71
73
|
this.socket.emit('subscribe', options, (response) => {
|
|
@@ -76,7 +78,9 @@ class QueueBitClient {
|
|
|
76
78
|
|
|
77
79
|
unsubscribe(options = {}) {
|
|
78
80
|
const subject = options.subject || 'default';
|
|
79
|
-
|
|
81
|
+
const queueName = options.queue || null;
|
|
82
|
+
const handlerKey = queueName ? `${subject}:${queueName}` : subject;
|
|
83
|
+
this.messageHandlers.delete(handlerKey);
|
|
80
84
|
|
|
81
85
|
return new Promise((resolve) => {
|
|
82
86
|
this.socket.emit('unsubscribe', options, (response) => {
|
|
@@ -95,8 +99,10 @@ class QueueBitClient {
|
|
|
95
99
|
|
|
96
100
|
handleMessage(message) {
|
|
97
101
|
const subject = message.subject || 'default';
|
|
98
|
-
const
|
|
99
|
-
|
|
102
|
+
const queueName = message.queueName || null;
|
|
103
|
+
const handlerKey = queueName ? `${subject}:${queueName}` : subject;
|
|
104
|
+
|
|
105
|
+
const handlers = this.messageHandlers.get(handlerKey);
|
|
100
106
|
if (handlers) {
|
|
101
107
|
for (const handler of handlers) {
|
|
102
108
|
handler(message);
|
|
@@ -109,4 +115,4 @@ class QueueBitClient {
|
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
|
|
112
|
-
module.exports = { QueueBitClient };
|
|
118
|
+
module.exports = { QueueBitClient, Queue: QueueBitClient };
|
package/src/client.js
CHANGED
package/src/server.js
CHANGED
|
@@ -10,7 +10,8 @@ class QueueBitServer {
|
|
|
10
10
|
|
|
11
11
|
this.messages = new Map();
|
|
12
12
|
this.subscribers = new Map();
|
|
13
|
-
this.
|
|
13
|
+
this.loadBalancers = new Map();
|
|
14
|
+
this.loadBalancerIdCounter = 0;
|
|
14
15
|
this.deliveryQueue = [];
|
|
15
16
|
this.deliveryBatchSize = 100;
|
|
16
17
|
this.isDelivering = false;
|
|
@@ -124,36 +125,42 @@ class QueueBitServer {
|
|
|
124
125
|
|
|
125
126
|
deliverMessage(message) {
|
|
126
127
|
const subject = message.subject || 'default';
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const socket = sockets[0];
|
|
135
|
-
sockets.push(sockets.shift());
|
|
136
|
-
|
|
137
|
-
socket.emit('message', message);
|
|
138
|
-
delivered = true;
|
|
139
|
-
|
|
140
|
-
if (message.removeAfterRead) {
|
|
141
|
-
this.removeMessage(message.id, subject);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
128
|
+
|
|
129
|
+
const loadBalancers = this.loadBalancers.get(subject);
|
|
130
|
+
if (loadBalancers && loadBalancers.size > 0) {
|
|
131
|
+
const activeLBs = [];
|
|
132
|
+
for (const [lbName, lb] of loadBalancers.entries()) {
|
|
133
|
+
if (lb.sockets.length > 0) {
|
|
134
|
+
activeLBs.push(lb);
|
|
144
135
|
}
|
|
145
136
|
}
|
|
137
|
+
|
|
138
|
+
if (activeLBs.length > 0) {
|
|
139
|
+
if (!this._lbRoundRobinIndex) this._lbRoundRobinIndex = {};
|
|
140
|
+
if (this._lbRoundRobinIndex[subject] === undefined) this._lbRoundRobinIndex[subject] = 0;
|
|
141
|
+
|
|
142
|
+
const idx = this._lbRoundRobinIndex[subject] % activeLBs.length;
|
|
143
|
+
this._lbRoundRobinIndex[subject]++;
|
|
144
|
+
|
|
145
|
+
const lb = activeLBs[idx];
|
|
146
|
+
const socket = lb.sockets[0];
|
|
147
|
+
lb.sockets.push(lb.sockets.shift()); // rotate within LB
|
|
148
|
+
|
|
149
|
+
socket.emit('message', { ...message, loadBalancerId: lb.id, queueName: lb.name });
|
|
150
|
+
|
|
151
|
+
// Always remove from store after LB delivery - LB messages are consumed, not persistent
|
|
152
|
+
this.removeMessage(message.id, subject);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
146
155
|
}
|
|
147
156
|
|
|
148
|
-
//
|
|
157
|
+
// Only deliver to regular subscribers if no load balancer handled it
|
|
149
158
|
const subscribers = this.subscribers.get(subject);
|
|
150
159
|
if (subscribers) {
|
|
151
160
|
for (const socket of subscribers) {
|
|
152
161
|
socket.emit('message', message);
|
|
153
|
-
delivered = true;
|
|
154
162
|
}
|
|
155
|
-
|
|
156
|
-
if (message.removeAfterRead && !queueGroups) {
|
|
163
|
+
if (message.removeAfterRead) {
|
|
157
164
|
this.removeMessage(message.id, subject);
|
|
158
165
|
}
|
|
159
166
|
}
|
|
@@ -161,20 +168,20 @@ class QueueBitServer {
|
|
|
161
168
|
|
|
162
169
|
handleSubscribe(socket, options, callback) {
|
|
163
170
|
const subject = options.subject || 'default';
|
|
164
|
-
const
|
|
171
|
+
const lbName = options.queue;
|
|
165
172
|
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
this.queueGroups.set(subject, new Map());
|
|
173
|
+
if (lbName) {
|
|
174
|
+
if (!this.loadBalancers.has(subject)) {
|
|
175
|
+
this.loadBalancers.set(subject, new Map());
|
|
170
176
|
}
|
|
171
177
|
|
|
172
|
-
const
|
|
173
|
-
if (!
|
|
174
|
-
|
|
178
|
+
const lbs = this.loadBalancers.get(subject);
|
|
179
|
+
if (!lbs.has(lbName)) {
|
|
180
|
+
lbs.set(lbName, { id: ++this.loadBalancerIdCounter, name: lbName, sockets: [] });
|
|
175
181
|
}
|
|
176
182
|
|
|
177
|
-
|
|
183
|
+
lbs.get(lbName).sockets.push(socket);
|
|
184
|
+
// Do NOT replay existing messages to load balancer subscribers
|
|
178
185
|
} else {
|
|
179
186
|
// Regular subscription (all subscribers get messages)
|
|
180
187
|
if (!this.subscribers.has(subject)) {
|
|
@@ -182,33 +189,38 @@ class QueueBitServer {
|
|
|
182
189
|
}
|
|
183
190
|
|
|
184
191
|
this.subscribers.get(subject).add(socket);
|
|
185
|
-
}
|
|
186
192
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
// Replay existing messages only for regular subscribers
|
|
194
|
+
const messages = this.messages.get(subject) || [];
|
|
195
|
+
for (const message of messages) {
|
|
196
|
+
if (!message.removeAfterRead) {
|
|
197
|
+
socket.emit('message', message);
|
|
198
|
+
}
|
|
192
199
|
}
|
|
193
200
|
}
|
|
194
201
|
|
|
195
202
|
if (callback) {
|
|
196
|
-
callback({
|
|
203
|
+
callback({
|
|
204
|
+
success: true,
|
|
205
|
+
subject,
|
|
206
|
+
loadBalancer: lbName,
|
|
207
|
+
loadBalancerId: lbName ? this.loadBalancers.get(subject)?.get(lbName)?.id : undefined
|
|
208
|
+
});
|
|
197
209
|
}
|
|
198
210
|
}
|
|
199
211
|
|
|
200
212
|
handleUnsubscribe(socket, options, callback) {
|
|
201
213
|
const subject = options.subject || 'default';
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
if (
|
|
205
|
-
const
|
|
206
|
-
if (
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
const index = sockets.indexOf(socket);
|
|
214
|
+
const lbName = options.queue;
|
|
215
|
+
|
|
216
|
+
if (lbName) {
|
|
217
|
+
const lbs = this.loadBalancers.get(subject);
|
|
218
|
+
if (lbs) {
|
|
219
|
+
const lb = lbs.get(lbName);
|
|
220
|
+
if (lb) {
|
|
221
|
+
const index = lb.sockets.indexOf(socket);
|
|
210
222
|
if (index > -1) {
|
|
211
|
-
sockets.splice(index, 1);
|
|
223
|
+
lb.sockets.splice(index, 1);
|
|
212
224
|
}
|
|
213
225
|
}
|
|
214
226
|
}
|
|
@@ -230,12 +242,12 @@ class QueueBitServer {
|
|
|
230
242
|
subscribers.delete(socket);
|
|
231
243
|
}
|
|
232
244
|
|
|
233
|
-
// Remove from all
|
|
234
|
-
for (const
|
|
235
|
-
for (const
|
|
236
|
-
const index = sockets.indexOf(socket);
|
|
245
|
+
// Remove from all load balancers
|
|
246
|
+
for (const lbs of this.loadBalancers.values()) {
|
|
247
|
+
for (const lb of lbs.values()) {
|
|
248
|
+
const index = lb.sockets.indexOf(socket);
|
|
237
249
|
if (index > -1) {
|
|
238
|
-
sockets.splice(index, 1);
|
|
250
|
+
lb.sockets.splice(index, 1);
|
|
239
251
|
}
|
|
240
252
|
}
|
|
241
253
|
}
|