I'm using a RabbitMQ queue to publish and receive messages between consumers, the main issue is that I want to receive a single message then exit. From other answers on this site I've seen that channel.get seems to be the best way to do this. However, I can't get it to work. This is the answer I've been using.
My current code:
var amqpChannel = null;
var queue = "test";
amqp.connect(cluster, (error0, connection) => {
if (error0) throw error0;
connection.createChannel((error1, channel) => {
if (error1) throw error1;
amqpChannel = channel;
});
});
var readMessage = function() {
if (amqpChannel)
{
amqpChannel.get(queue, (err, msg) => {
if (err) console.log(err);
if (msg)
{
console.log("Message received: " + msg.content.toString());
amqpChannel.ack(msg);
}
});
}
}
setTimeout(readMessage, 1000);
As far as I can see, it is identical to the code in the accepted answer above, however I can't seem to get it to work. What am I missing?
Edit: Extra info
Using channel.consume works for me, it gets whatever messages are in the queue. However using the channel.get method results in nothing. I have used console.log lines to ensure the channel is being created properly, but for some reason the channel.get callback is never being triggered. I know that all the connection and queue creation is all working, I just can't seem to get the channel.get method to trigger at all.
Edit 2: I found the issue
My callback function wasn't correct. Looking at the documentation here, channel.get requires an options parameter before the callback, adding that in fixed my issue. My working code is now:
var amqpChannel = null;
var queue = "test";
amqp.connect(cluster, (error0, connection) => {
if (error0) throw error0;
connection.createChannel((error1, channel) => {
if (error1) throw error1;
amqpChannel = channel;
});
});
var readMessage = function() {
if (amqpChannel)
{
amqpChannel.get(queue, {noAck: true}, (err, msg) => {
if (err) console.log(err);
if (msg)
{
console.log("Message received: " + msg.content.toString());
amqpChannel.ack(msg);
}
});
}
}
setTimeout(readMessage, 1000);
Related
I am currently building a load balancing tool to take different stress measurements of how servers handle http protocol requests. Currently, I can send just a ton, but I cannot process them all. That is why I am looking to put it all into a message queue like rabbit, using AMQP.
My current code https://github.com/yugely/Stork
I am currently using an event loop and a timeout to adequately do what I am intending to accomplish.
I want to use RabbitMQ by using one of my current loggers to "emit" a message into the message queue. I don't know how to modularize it so I don't have to constantly create channels as all the tutorials seem to just copy paste each other without going into how to use it in external files.
I'm hoping someone can either lead me to what I may be duping. I'm unsure of how to even ask this question. I have been looking at RabbitMQ and AMQP to handle a message queue for a project. The issue I am facing is that I don't know how to send a message to rabbit mq. Let me illustrate what I am understanding by doing a copypasta of the first tutorial on the rabbitmq site:
send.js
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
var queue = 'hello';
var msg = 'Hello World!';
channel.assertQueue(queue, {
durable: false
});
/*
How do I do this outside the functions so receive gets it? How can I call a method/function to send a message to an existing queue through an existing channel?
*/
channel.sendToQueue(queue, Buffer.from(msg));
console.log(" [x] Sent %s", msg);
});
setTimeout(function() {
connection.close();
process.exit(0);
}, 500);
});
receive.js
var amqp = require('amqplib/callback_api');
amqp.connect('amqp://localhost', function(error0, connection) {
if (error0) {
throw error0;
}
connection.createChannel(function(error1, channel) {
if (error1) {
throw error1;
}
var queue = 'hello';
channel.assertQueue(queue, {
durable: false
});
console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue);
/*
How do I set up a consumer outside this function?
*/
channel.consume(queue, function(msg) {
console.log(" [x] Received %s", msg.content.toString());
}, {
noAck: true
});
});
});
For the sender, do I just always create a new channel for every message?
All the tutorials accept arguments when you run the "node" terminal command. The only way I can currently see this working is to use the "child process" library, but that would be bad news bears right? As wouldn't that create another nodejs cluster?
If I have to use a client to send messages, am I able to use axios? (I've seen some people claiming they are able to but I don't know for sure). Since it will be using the amqp protocol, what is the amqp client?
Or are these queues like instantiated in the entry file? Like you set up the queue when you run your entry point command and then allow the different "events" to send messages to the queue?
How can I modularize this?
Just to illustrate, here is my current axios code
RadioFlyer.js
await axios(flight.journal.actions[modkey]).then(function (response) {
reaction.key.type = messageType.HTTPResponse.Okay
reaction.message = response === undefined ? response : "no response"
let smaug = typeof reaction.message.status === "undefined" ? reaction.message : reaction.message.status
flight.journal.reactions.push(reaction)
let pulse = {
id: flight.id + "-" + index,
timestamp: Date.now(),
body: {
payload: {
protocol : reaction.protocol,
type: messageType.HTTPResponse.Okay,
url: flight.journal.actions[modkey].baseURL,
status: smaug
}
}
}
/*
Can I emit a messaging event to my logger
*/
//emit
seidCar.HTTPLogger.emit("logHttp", reaction)
//emit
seidCar.HeartbeatLogger.emit("pulse", pulse)
}).catch(function (error) {
reaction.key.type = messageType.HTTPResponse.Error
reaction.message = error.response === undefined ? error.code : error.response
let smaug = typeof reaction.message.status === "undefined" ? reaction.message : reaction.message.status
let pulse = {
id: flight.id + "-" + index,
timestamp: Date.now(),
body: {
payload: {
protocol : reaction.protocol,
type: messageType.HTTPResponse.Error,
url: flight.journal.actions[modkey].baseURL,
status: smaug
}
}
}
let err = {
id: flight.id+"-"+index+"-ERROR",
timestamp : Date.now(),
fatal : false,
potentialFix : "Examine Http Call with id: " + flight.id + "-" + index,
body: {
payload: {
protocol : reaction.protocol,
type: messageType.HTTPResponse.Error,
url: flight.journal.actions[modkey].baseURL,
status: smaug
}
}
}
flight.journal.reactions.push(reaction)
//emit
seidCar.HTTPLogger.emit("logHttp", reaction)
//emit
seidCar.ErrorLogger.emit("logError", err)
//emit
seidCar.HeartbeatLogger.emit("pulse", pulse)
})
And have my logger handler the sending to the queue?
HTTPLogger.js
/*
Can I now send the message to the queue here, in this file?
*/
const HTTPEventLogger = require("events")
const emitter = new HTTPEventLogger()
const errorEmitter = require("./ErrorLogger").Emitter
class HTTPLogger extends HTTPEventLogger {
logHttp(message) {
switch (message.key.type) {
case "ERROR":
if (message !== undefined) {
console.log(message)
} else {
errorEmitter.emit("tossIt", {
error:"HTTP error Message is undefined in ~/Homestasis/Agent/HTTPLoggerjs.",
poi:"check for recent additions to pilot agents in ~/Pilots/Agent",
timestamp: Date.now(),
potentialFix:"look to where you are emitting the message. Function Scope"
})
}
break;
case "OKAY":
if (message !== undefined) {
console.log(message)//bState.message.status)
} else {
errorEmitter.emit("tossIt", {
error:"HTTP okay Message is undefined in ~/Homestasis/Agent/HTTPLoggerjs.",
poi:"check for recent additions to pilot agents in ~/Pilots/Agent",
timestamp: Date.now(),
potentialFix:"look to where you are emitting the message. Function Scope"
})
}
break;
default:
errorEmitter.emit("tossIt", "this is a tossIt error log. No http key type was caught bSate = " + bState)
}
}
}
var logger = new HTTPLogger()
emitter.on("logHttp",logger.logHttp)
exports.Emitter = emitter
Thats how I'd like to send the message.
I'd like to be able to receive it much the same way
I'm not sure how to implement it given how I perceive it currently working and I am missing a key part of the equation. Thank you for your time!
I decided to not use RabbitMQ.
For node, RSMQ is what I am going with. I realized I fundamentally needed to shift how I viewed message queues working practically to get them to work practically. I am using RSMQ, as I can understand how it fits into how I built my project out. I was going with ZeroMQ at first (I still may, the pliability of sockets on steroids). But, I do like the actual features and stuff when I actually think about using it like a "mainline nerve".
The way I want to build it out goes as such:
Axios Makes the call, fires off an event to the httplogger which fires off an event to the message queue. So:
RadioFlyer.js > HttpLogger.js > RSMQ-Messagequeue.js > next things > and next...
RSMQ-Messagequeue.js acts as a "middle man" or an intermediary to put the stops on the wheels when overloaded.
The pros of RSMQ is that I also intend to implement a Redis Cache (I could implement ZeroMQ and do much the same), but I like that RSMQ allows for more queue options, instead of 1 queue with many topics.
There is a more fleshed out answer.
I have strange error generation function.. It is from HttpRequest like this
public async request(method, url, data, headers = {}){
let init { method: method };
if (data) {
let payload: string = JSON.stringify(data);
init.body = payload;
}
if (this.key) headers["x-auth-token"] = this.key;
headers["Content-Type"] = "application/json";
init.headers = headers;
let result = await fetch(url, init);
if (result.status == 200) return result;
throw { status: result.status, message: result.statusText };
}
Now, I am trying to catch with something like this:
try {
let img = await HttpRequest.request("GET", "/login");
let text = await img.text();
let str = "data:image/jpeg;base64, " + text;
this.setState({ avatar: str });
} catch (err) {
console.log("log here");
}
What is strange, was that nothing catched, even though I deliberately made an error, no "log here" shows anywhere in console.
But if I change it like this:
try {
let img = await HttpRequest.request("GET", "/login");
let text = await img.text();
let str = "data:image/jpeg;base64, " + text;
this.setState({ avatar: str });
} catch (err) {
console.error("log here"); // <--- console.error() instead of console.log
}
Then the console showed "log here" error. It is strange that difference between console.log and console.error inside same catch clause treated different way.
Can anyone help me here, where did I do wrong?
Thank you
EDIT: If it made difference, the code behaved correctly before, when I throw the error as throw "ERROR " + result.status + ": " + result.statusText; at my request() function. It was back to normal when I changed it back to throw string instead of object.
Well.. I am not sure if this is the answer, or proper answer, but the question turned out not programming or javascript nature.
After I tried Parth Savaliya's comment, I had some ideas, and I tried to make this very simple function
function testError() {
throw {aa: 'test', bb: 'bb'};
}
try {
console.log("nothing here");
testError();
} catch (err) {
console.log(err);
console.log('foo');
}
and every time I feed different object, any console command, including console.error() gave me different result. The previous console.error() was just luck. It was varying from unintelligible mumble jumble, various strings, to no output, and finally crashed Google Chrome. There was no pattern, and seemed random. I also tried to replace console.log() in catch clause with an XHR request to server, and it worked fine. Firefox worked fine. So I conclude that it was chrome's console that was having problem. Tried to remove all extensions, cleaned cache and everything, reinstall chrome, and it was solved.
I think you're currently viewing only error logs, like
this...!
Please click on the first item on the list, to show all types of log, like
this...!
So, I am currently appending to my web page and or removing from it based on values stored in an array.
But in order for them to appear I need to reload the page, but I need it all to happen on the fly without reloading the page. So as soon as I append I can see it, same goes for when I remove it.
I have been using socket.io server side code to try and do it but I have had mixed results. Am not too experienced with socket.io and still trying to get to grips with it.
Am literally just trying to emit data based on what is stored in the array chanArr in real time by appending it to the table.
app.js file
This is my server side code for socket.io, the function updateSip is what is emitting the data back to the client side.
var ari = require('ari-client');
var util = require('util');
var chanArr = [];
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
//ARI client
ari.connect('http://localhost:8088', 'asterisk', 'asterisk', clientLoaded);
function clientLoaded(err, client) {
if (err) {
throw err;
}
// find or create a holding bridges
var bridge = null;
client.bridges.list(function (err, bridges) {
if (err) {
throw err;
}
bridge = bridges.filter(function (candidate) {
return candidate.bridge_type === 'mixing';
})[0];
if (bridge) {
console.log(util.format('Using bridge %s', bridge.id));
} else
client.bridges.create({
type : 'mixing'
}, function (err, newBridge) {
if (err) {
throw err;
}
bridge = newBridge;
console.log(util.format('Created bridge %s', bridge.id));
});
}
});
// handler for StasisStart event
function stasisStart(event, channel) {
console.log(util.format(
'Channel %s just entered our application, adding it to bridge %s',
channel.name,
bridge.id));
channel.answer(function (err) {
if (err) {
throw err;
}
bridge.addChannel({
channel : channel.id
}, function (err) {
var id = chanArr.push(channel.name)
updateSip();
console.log("User: " + channel.name);
if (err) {
throw err;
}
//If else statement to start music for first user entering channel, music will stop once more than 1 enters the channel.
if (chanArr.length <= 1) {
bridge.startMoh(function (err) {
if (err) {
throw err;
}
});
} else {
bridge.stopMoh(function (err) {
if (err) {
throw err;
}
});
}
});
});
}
// handler for StasisEnd event
function stasisEnd(event, channel) {
console.log(util.format(
'Channel %s just left our application', channel.name));
console.log(channel.name);
var index = chanArr.indexOf(channel.name);
chanArr.splice(index, 1);
updateSip();
}
client.on('StasisStart', stasisStart);
client.on('StasisEnd', stasisEnd);
client.start('bridge-hold');
}
//Socket.io logic here
server.listen(3009, function () {
console.log('listening on *:3009');
});
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
res.sendfile(__dirname + "/testPage.html");
});
io.sockets.on('connection', function () {
updateSip();
});
io.sockets.on('updateSip', function () {
console.log('Being called!')
updateSip();
});
function updateSip() {
io.sockets.emit('sip', chanArr);
}
test.js
Client side JQuery and socket.io code and where am appending elements to the web page.
jQuery(function ($) {
var socket = io.connect();
var $sip = $('#sip');
socket.on('sip', function (data) {
var sip = '';
for (i = 0; i < data.length; i++) {
sip += data[i]
if(sip){
$sip.append('<tr>\
<td>' + sip + '</td>\
<td><input type="checkbox" data-on="Voice" data-off="Muted" checked data-toggle="toggle" data-onstyle="success" data-offstyle="danger"></td>\
<td><button class="btn btn-default kick" id="kick" data-toggle="modal" data-target="#myModal" type="submit">Kick</button></td>\
</tr>');
}
else{
$sip.append('Currently no extensions');
}
sip = '';
}
});
});
testPage.html
What I am appending too.
<div class="secondary-bridge">
<h3 class="conf-head">Conference call:</h3>
<table class="table table-bordered">
<thead>
<tr>
<th>Extension</th>
<th>Mute</th>
<th>Kick</th>
</tr>
</thead>
<tbody id ="sip">
</tbody>
</table>
EDIT:
So at the moment, I have to refresh the page so the page runs the updateSip function, to then emit and display the new values which have been appended, every time I add, or remove a value from the array which handles what to display I have to refresh the page.
Page loads -> Array[]-> Call, inserts value in array-> Array['SIP-448']-> Reload page ->Can now see it appended.
I am trying to get it to work so I never have to refresh.
First thing I tried before asking for some advice was the method #show-me-the-code suggested and the following happens, it does do kinda what I want it to do but after 1 value, it breaks, and values when they are removed from the array also still stay displayed on the webpage:
1 Value
More than 1 value
When I refresh the page it then goes to this:
When I then try to remove a value from the array it then appends the previous value, once I refresh the page there is only one value which is the on which should remain.
Which is my big issue I dont know why its acting the way it is currently.
Here's what I would do. On the server side where your chanArr changes (elements are added or removed), trigger an emit('sip', chanArr) call.
// right after adding channel to the array
chanArr.push(channel.name);
updateSip();
// also after removing it from the array if it is in different function
chanArr.splice(index, 1);
updateSip();
If I understood you correctly you want to update data of your server to your clientside.
The problem with your code is that you only call the function updateSip() on client-connection. If you want to update it somewhen else you have to provide more code to your app.js
You might, for instance, create an event listener like
io.sockets.on('updateSip', function () {
updateSip();
});
and on your clientside you might create a function like
function updateSip() {
socket.emit('updateSip');
},
that way your server answers with the information you need if you call the function updateSip() in your test.js
I'm encountering few problems when dealing with mongoose.
I wrote the following snippet of code:
if (!usr.settings) usr.settings = {};
async.forEach(Object.keys(params), function (item, nextitem){
usr.settings[item] = params[item];
nextitem();
}, function (err) {
if (err) return callback(err);
usr.save(function(err) {
if (err) return callback(err);
return callback();
});
});
When I first use it, it works just fine, it creates all the items in the document perfectly, but when I use it the 2nd time (lets say I want to update those items) it doesn't change their values nor gives me any error.
The data stays just the same.
I tried to debug it.
if (!usr.settings) usr.settings = {};
async.forEach(Object.keys(params), function (item, nextitem){
usr.settings[item] = params[item];
nextitem();
}, function (err) {
if (err) return callback(err);
usr.save(function(err) {
console.log(usr); <------------------- [At this point it shows the updated data but for some reason it doesnt save it to the db]
if (err) return callback(err);
return callback();
});
});
Any idea why it could happen?
Thanks.
After messing with it, found the solution.
lets say you are using the following schema:
user_schema = {
settings = Object
}
you won't be able to set setting., therefore the following code:
usr.settings.something = 123;
usr.save(function(err) {
if (err) return callback(err);
return callback();
});
will do nothing and still will not result in an error.
If you want to fix the issue, apply this fix to the schema:
user_schema = {
settings = {
something : Number
something_else: Object
}
}
now you will be able to set something with a value :)
I wish it would helpful to someone
I created code like this for getting news from xml export from another website and I am trying to fill with it my database.
function UpdateLunchTime() {
var httpRequest = require('request');
var xml2js = require('xml2js');
var parser = new xml2js.Parser();
var url = 'http://www...com/export/xml/actualities';
httpRequest.get({
url: url
}, function(err, response, body) {
if (err) {
console.warn(statusCodes.INTERNAL_SERVER_ERROR,
'Some problem.');
} else if (response.statusCode !== 200) {
console.warn(statusCodes.BAD_REQUEST,
'Another problem');
} else {
//console.log(body);
parser.parseString(body, function (err2, result) {
//console.log(result.Root.event);
var count = 0;
for (var i=0;i<result.Root.event.length;i++)
{
//console.log(result.Root.event[i]);
InsertActionToDatabase(result.Root.event[i]);
}
/*
result.Root.event.forEach(function(entry) {
InsertActionToDatabase(entry);
});
*/
});
}
});
}
function InsertActionToDatabase(action)
{
var queryString = "INSERT INTO Action (title, description, ...) VALUES (?, ?, ...)";
mssql.query(queryString, [action.akce[0], action.description[0],...], {
success: function(insertResults) {
},
error: function(err) {
console.log("Problem: " + err);
}
});
}
For individual actualities it's working fine but when I run it over whole xml I get this error:
Error: [Microsoft][SQL Server Native Client 10.0][SQL Server]Resource ID : 1. The request limit for the database is 180 and has been reached. See 'http://go.microsoft.com/fwlink/?LinkId=267637' for assistance.
And for a few last objects I get this error:
Error: [Microsoft][SQL Server Native Client 10.0]TCP Provider: Only one usage of each socket address (protocol/network address/port) is normally permitted.
Thanks for help
The problem is that you're trying to make too many concurrent (insert) operations in your database. Remember that in node.js (almost) everything is asynchronous, so when you call InsertActionToDatabase for one of the items, this operation will start right away and not wait before it finishes to return. So you're basically trying to insert all of the events at once, and as the error message said there's a limit on the number of concurrent connections which can be made to the SQL server.
What you need to do is to change your loop to run asynchronously, by waiting for one of the operations to complete before starting the next one (you can also "batch" send a smaller number of operations at once, continuing after each batch is complete, but the code is a little more complicated) as shown below.
var count = result.Root.event.length;
var insertAction = function(index) {
if (index >= count) return;
InsertActionToDatabase(result.Root.event[i], function() {
insertAction(index + 1);
});
}
insertAction(0);
And the InsertActionToDatabase function would take a callback parameter to be called when it's done.
function InsertActionToDatabase(item, done) {
var table = tables.getTable('event');
table.insert(item, {
success: function() {
console.log('Inserted event: ', item);
done();
}
});
}