amqp exchanges are not auto-deleted - node.js

I have rabbitmq 3.3.5 running and nodejs project based on amqplib 0.2.1
The problem is that once exchange has been asserted it does not deleted after connection to rabbitmq is closed.
If we start such example
var amqp = require('amqplib');
var when = require('when');
amqp.connect('amqp://localhost').then(function(conn) {
return when(conn.createChannel().then(function(ch) {
var ex = 'logs_new';
var ok = ch.assertExchange(ex, 'fanout', {durable: false, autoDelete: true}})
var message = process.argv.slice(2).join(' ') ||
'info: Hello World!';
return ok.then(function() {
ch.publish(ex, '', new Buffer(message));
console.log(" [x] Sent '%s'", message);
return ch.close();
});
})).ensure(function() { conn.close(); });
}).then(null, console.warn);
and run
#rabbitmqctl list_exchanges
Listing exchanges ...
amq.rabbitmq.log topic
amq.rabbitmq.trace topic
amq.topic topic
logs_new fanout
...done.
Though connection to rabbitmq was closed, but exchange (logs_new) still persists.
How to tell rabbitmq that exchange needs to be deleted if it is not used by any connection ?
edit:
As it is stated at http://www.squaremobius.net/amqp.node/doc/channel_api.html autoDelete option should be 'true'. but nevertheless even exchange with new name is not deleted. What could be wrong?

You should set the auto_delete flag to True when declaring the exchange. This will automatically delete the exchange when all channels are done with it.
Keep in mind that this means that it will stay as long as there is an active binding to the exchange. If you delete the binding, or queue, the exchange will be deleted.
If you need to keep the queue, but not the exchange you can remove the binding once you are done publishing. This should automatically remove the exchange.

Related

How does a ws websocket tell if connection event corresponds to an existing client in the clients list?

I am using the ws module to implement a WebSocket server in NodeJS. On the client-side, I request the connection using
webSocket = new WebSocket(url);
On the server-side, I have code that handles the 'connect' event, in which I print out the number of clients using
console.log("Total number of clients = " + wsServer.clients.size);
If I open the client-side in different tabs (or browsers), the number of clients is incremented for each new connection (as expected).
If I refresh a page, the webSocket = new WebSocket(url); code is called again and on the server the code handling the 'connect' event (see below) is also called again. However, in this case, the number of clients is not incremented. This is nice behaviour as it maintains the number of connections one wants, but I cannot see how this is done. I want to be able to test if this is an existing connection as I have a chat room running that says 'so-and-so' has joined when a new connection is made. However, I don't want this to happen every time a user refreshes their page.
Here is the server-side event-handler:
// On connection
wsServer.on("connection", function (ws, req) {
let { query } = url.parse(req.url, true);
ws.userName = ("name" in query) ? query.name : null;
ws.roomCode = ("roomCode" in query) ? query.roomCode : null;
ws.userPIN = ("PIN" in query) ? query.PIN : null;
console.log(ws.userName + " joined room " + ws.roomCode);
console.log("Total number of clients = " + wsServer.clients.size);
let data = {
userName: "Server",
message: ws.userName + " joined the room."
};
let dataStr = JSON.stringify(data);
// Loop through each client
wsServer.clients.forEach((client) => {
// Check if client is ready and in same room
if ((client.readyState === WebSocket.OPEN) && (client.roomCode == ws.roomCode)) {
client.send(dataStr);
}
});
// On message event
ws.on("message", function (msg) {
// Loop through each client
wsServer.clients.forEach((client) => {
// Check if client is ready and in same room
if ((client.readyState === WebSocket.OPEN) && (client.roomCode == ws.roomCode)) {
client.send(msg.toString());
}
});
});
});
As one can see, I do modify the client by adding name and room code fields to it but these are not present when the 'connect' event fires, implying the object is being created from scratch. However, no extra client is being added to the clients list, so what I would like to understand is:
how does the ws package know this is an existing connection?
how can I test for this?
Any advice would be gratefully received!
On investigation, it turns out the when the user refreshes the page, the WebSocket connection is closed and then re-opened. Hence there is no testing for an existing client, the existing client is just deleted on the server and the new connection added - hence the number of clients does not appear to change.

how can I make private chat rooms with sockjs?

I am trying to make a chat system where only two users are able to talk to each other at a time ( much like facebook's chat )
I've tried multiplexing, using mongoDB's _id as the name so every channel is unique.
The problem I'm facing is that I cannot direct a message to a single client connection.
this is the client side code that first sends the message
$scope.sendMessage = function() {
specificChannel.send(message)
$scope.messageText = '';
};
this is the server side receiving the message
specificChannel.on('connection', function (conn) {
conn.on('data', function(message){
conn.write('message')
}
}
When I send a message, to any channel, every channel still receives the message.
How can I make it so that each client only listens to the messages sent to a specific channel?
It appeared that SockJS doesn't support "private" channels. I used the following solution for a similar issue:
var channel_id = 'my-very-private-channel'
var connection = new SockJS('/pubsub', '')
connection.onopen = function(){
connection.send({'method': 'set-channel', 'data': {'channel': channel_id}})
}
Backend solution is specific for every technology stack so I can't give a universal solution here. General idea is the following:
1) Parse the message in "on_message" function to find the requested "method name"
2) If the method is "set-channel" -> set the "self.channel" to this value
3) Broadcast further messages to subscribers with the same channel (I'm using Redis for that, but it also depends on your platform)
Hope it helps!

Broadcast rabbitMq messages with ServiceStack

Is there way to make method
myMessageService.CreateMessageQueueClient().Publish(myMessage);
broadcast messages to all receivers?
The problem is, that RegisterHandler<T> internally uses the type of T to build the queue name it listens to. So the only chance you have is to go off the track with the following solution by utilizing a custom fanout exchange to multiple queues:
var fanoutExchangeName = string.Concat(QueueNames.Exchange,
".",
ExchangeType.Fanout);
At some point of your system you have to ensure the exchange with the following code:
var rabbitMqServer = new RabbitMqServer();
var messageProducer = (RabbitMqProducer) rabbitMqServer.CreateMessageProducer();
var channel = messageProducer.Channel; // you can use any logic to acquire a channel here - this is just a demo
channel.ExchangeDeclare(fanoutExchangeName,
ExchangeType.Fanout,
durable: true,
autoDelete: false,
arguments: null);
Now we can publish messages to this fanout:
var message = new Message<T>(yourInstance);
messageProducer.Publish(QueueNames<T>.In, // routing key
message, // message
fanoutExchangeName); // exchange
So now the message gets published to our exchange, but we need to bind queues to the exchange in the consuming components, which we do with:
var rabbitMqServer = new RabbitMqServer();
var messageQueueClient = (RabbitMqQueueClient) rabbitMqServer.CreateMessageQueueClient();
var channel = messageQueueClient.Channel; // you just need to get the channel
var queueName = messageQueueClient.GetTempQueueName();
channel.QueueBind(queueName, // queue
fanoutExchangeName, // exchange
QueueName<T>.In); // routing key
The queue is automatically deleted after the last (and only) consumer disconnects and will not survive a restart of RabbitMq.
The hacky part is now the listening though ...
var consumer = new RabbitMqBasicConsumer(channel);
channel.BasicConsume(queueName,
false,
consumer);
Task.Run(() =>
{
while (true)
{
BasicGetResult basicGetResult;
try
{
basicGetResult = consumer.Queue.Dequeue();
}
catch (EndOfStreamException endOfStreamException)
{
// this is ok
return;
}
catch (OperationInterruptedException operationInterruptedException)
{
// this is ok
return;
}
catch (Exception ex)
{
throw;
}
var message = basicGetResult.ToMessage<T>();
// TODO processing
}
});
This solution does not provide any auto-reconnect, filters, or other stuff though.
A basic walkthrough is available here.
Edit:
One thing that just got to my mind: You can use a ServiceStack.Messaging.MessageHandler<T> instance to provide replies and retries with ease.

WebRTC - How to establish a peer connection after offers and answers

I have a node.js running which the users will connect to. The offer and answer will be generated and sent through node.js.
I'm trying to establish a peer connection and send over a camera stream. I tried my code without using ICE candidates as the computers where in the same subnet. I tried to implement ICE afterwards. I'm not sure if i've done it right though or if it's even needed if the computers are on the same subnet.
var localStream;
//Connect to signaling server
var signalingChannel = io.connect('http://85.134.54.193:8001');
console.log("Connect to signaling server");
var servers = null;
var video1;
var video2;
var audio1;
var audio2;
var cfg = {"iceServers":[{"url":"stun:stun.l.google.com:19302"}]};//{ "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] };
var con = { 'optional': [{'DtlsSrtpKeyAgreement': true}, {'RtpDataChannels': true }] };
var peerConnection;
//Runs after the page has been loaded
window.onload=function(){
//Gets ID for the video element which will display the local stream
video1 = document.getElementById("audio1");
//Gets ID for the video element which will display the remote stream
video2 = document.getElementById("audio2");
audio1 = document.getElementById("audio1");
audio2 = document.getElementById("audio2");
}
//Start button function
function caller(){
peerConnection = new webkitRTCPeerConnection(cfg);
navigator.webkitGetUserMedia({'audio':true, video:true}, function (stream) {
console.log("Got local audio", stream);
video1.src = window.webkitURL.createObjectURL(stream)
peerConnection.addStream(stream);
},
function ( err ) {
console.log( 'error: ', err );
});
console.log("Calling");
//Create Offer
peerConnection.createOffer(function (offerDesc) {
console.log("Created local offer", offerDesc.sdp);
peerConnection.setLocalDescription(offerDesc);
}, function () { console.warn("Couldn't create offer"); });
//ICE Candidates Generator
peerConnection.onicecandidate = function(evt) {
//When The Ice Gathering is complete
if (evt.target.iceGatheringState == "complete") {
//Create a new offer with ICE candidates
peerConnection.createOffer(function(offer) {
console.log("Offer with ICE candidates: " + offer.sdp);
signalingChannel.emit('offer', JSON.stringify(offer));
console.log("offer sent");
signalingChannel.on('answer', function(data){
console.log("Receive answer");
//The answer is set as the remote description for the offerer
peerConnection.setRemoteDescription(new RTCSessionDescription(JSON.parse(data)));
console.log("Set remote desc");
peerConnection.onaddstream = gotRemoteStream;
console.log("Add remote stream to peer connection");
});
});
}
}
}
function answerer(){
peerConnection = new webkitRTCPeerConnection(cfg);
navigator.webkitGetUserMedia({'audio':true, video:true}, function (stream) {
console.log("Got local audio", stream);
video1.src = window.webkitURL.createObjectURL(stream)
peerConnection.addStream(stream);
},
function ( err ) {
console.log( 'error: ', err );
});
console.log("Answering");
//Listen for offer
signalingChannel.on('offer', function(data){
console.log("Offer Received");
//Set the remote description from caller's local description
peerConnection.setRemoteDescription(new RTCSessionDescription(JSON.parse(data)));
//Generate answer after getting the remote description
peerConnection.createAnswer(function(sessionDescription) {
//Set local description
peerConnection.setLocalDescription(sessionDescription);
//The local desc will be the answer sent back to offerer
signalingChannel.emit('answer', JSON.stringify(sessionDescription));
console.log("Answer sent");
});
});
}
function gotRemoteStream(event){
video2.src = window.webkitURL.createObjectURL(event.stream);
}
Here is a sequence of events I have working today (Feb 2014) in Chrome. This is for a simplified case where peer 1 will stream video to peer 2.
Set up some way for the peers to exchange messages. (The variance in how people accomplish this is what makes different WebRTC code samples so incommensurable, sadly. But mentally, and in your code organization, try to separate this logic out from the rest.)
On each side, set up message handlers for the important signalling messages. You can set them up and leave them up. There are 3 core messages to handle & send:
an ice candidate sent from the other side ==> call addIceCandidate with it
an offer message ==> SetRemoteDescription with it, then make an answer & send it
an answer message ===> SetRemoteDescription with it
On each side, create a new peerconnection object and attach event handlers to it for important events: onicecandidate, onremovestream, onaddstream, etc.
ice candidate ===> send it to other side
stream added ===> attach it to a video element so you can see it
When both peers are present and all the handlers are in place, peer 1 gets a trigger message of some kind to start video capture (using the getUserMedia call)
Once getUserMedia succeeds, we have a stream. Call addStream on the peer 1's peer connection object.
Then -- and only then -- peer 1 makes an offer
Due to the handlers we set up in step 2, peer 2 gets this and sends an answer
Concurrently with this (and somewhat obscurely), the peer connection object starts producing ice candidates. They get sent back and forth between the two peers and handled (steps 2 & 3 above)
Streaming starts by itself, opaquely, as a result of 2 conditions:
offer/answer exchange
ice candidates received, exchanged, and added
When I want to change the stream, I go back to step 3 and set up a new peer connection object and do the whole offer/answer again.
Why do you wait for ICE to complete before creating an answer? what about doing them simultaneously? That might help, as it is just meant to work simultaneously. If you can post your logs after this when it would still not work we can try debugging it even further. If you want to see an audio-only example of this (it sends both music-audio and microphone-audio) check here, and the github source. Server made with node.js and ws plugin. The audio connection works with webRTC.

Node-amqp example of Topic exchange

Does anyone have an example of creating a topic exchange in Node-amqp? I've already gone through https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/javascript-nodejs but unfortunately it doesn't recreate tutorials 4+ from the RabbitMQ website.
This might be an over simplistic answer but at a basic level it's doable like this...
var amqp = require('amqp');
var connection = amqp.createConnection({ host: '127.0.0.1' });
connection.on('ready', function () {
var exchange = connection.exchange('my-super-xchange', {type: 'topic'});
exchange.on('open', function(){
console.log('Lets do this!');
})
})
Once you have run the above, the exchange should now be visible on your rabbitMQ instance
$ rabbitmqctl list_exchanges
Listing exchanges ...
direct
amq.direct direct
amq.fanout fanout
amq.headers headers
amq.match headers
amq.rabbitmq.log topic
amq.rabbitmq.trace topic
amq.topic topic
dingo topic
my-super-xchange topic
...done.

Resources