How to use long polling in native JavaScript and node.js? - javascript

I need to implement long polling for a chat application. I've searched around, but I only find how to implement it in JavaScript using JQuery. How can I implement it using only native JavaScript and node.js? Can you guide me to some relevant articles or materials?

Q: How to do long polling in native Javascript in nodeJS?
A: I guess first of all you need to understand how the long polling model works. If you haven't had any clue then the RFC-6202 specification is a good starting point.
It is about the client sending a request to the server and waits until a response is returned.
From the specification we know that first the client will have to issue a http request which has an infinite or at least a high timeout value. Then the server, which is your nodeJs application is expected to stash all incoming requests into a data structure, basically a holding area. Your application will essentially hold on all the response object until an event gets triggered, then you reply to the responses appropriately.
Consider this Pseudo code:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
var requestCounter = 0;
var responses = {
/* Keyed by room Id =*/
"room_abc" : [ /* array of responses */]
};
app.get('/', function (req, res) {
requestCounter += 1;
var room = /* assuming request is for room_abc */ "room_abc";
// Stash the response and reply later when an event comes through
responses[room].push(res);
// Every 3rd request, assume there is an event for the chat room, room_abc.
// Reply to all of the response object for room abc.
if (requestCounter % 3 === 0) {
responses["room_abc"].forEach((res) => {
res.send("room member 123 says: hi there!");
res.end();
});
}
});
app.use(bodyParser.text({ type: 'text/*' }));
app.use(bodyParser.json());
app.listen(9999, function () {
console.log('Example app listening on port 9999!')
})
It is relatively time consuming to write a working example here but the code above is a good example of how you can implement long polling in NodeJS.
If you have postman installed or curl you can do HTTP calls to http://localhost:9999/ using method GET. You should noticed that on the first two calls you won't get a response and it is when you fired the 3rd one then you'll receive a response for all previous and current calls.
The idea here is you stash the request's response object first and when an event comes through, assuming on every 3rd HTTP call, you then loop through all of the responses and reply to them. For your chat application's case, the event that triggers a response would probably be when someone fires off a message to a chat room.

Related

WebSocket needs browser refresh to update list

My project works as intended except that I have to refresh the browser every time my keyword list sends something to it to display. I assume it's my inexperience with Expressjs and not creating the route correctly within my websocket? Any help would be appreciated.
Browser
let socket = new WebSocket("ws://localhost:3000");
socket.addEventListener('open', function (event) {
console.log('Connected to WS server')
socket.send('Hello Server!');
});
socket.addEventListener('message', function (e) {
const keywordsList = JSON.parse(e.data);
console.log("Received: '" + e.data + "'");
document.getElementById("keywordsList").innerHTML = e.data;
});
socket.onclose = function(code, reason) {
console.log(code, reason, 'disconnected');
}
socket.onerror = error => {
console.error('failed to connect', error);
};
Server
const ws = require('ws');
const express = require('express');
const keywordsList = require('./app');
const app = express();
const port = 3000;
const wsServer = new ws.Server({ noServer: true });
wsServer.on('connection', function connection(socket) {
socket.send(JSON.stringify(keywordsList));
socket.on('message', message => console.log(message));
});
// `server` is a vanilla Node.js HTTP server, so use
// the same ws upgrade process described here:
// https://www.npmjs.com/package/ws#multiple-servers-sharing-a-single-https-server
const server = app.listen(3000);
server.on('upgrade', (request, socket, head) => {
wsServer.handleUpgrade(request, socket, head, socket => {
wsServer.emit('connection', socket, request);
});
});
In answer to "How to Send and/or Stream array data that is being continually updated to a client" as arrived at in comment.
A possible solution using WebSockets may be to
Create an interface on the server for array updates (if you haven't already) that isolates the array object from arbitrary outside modification and supports a callback when updates are made.
Determine the latency allowed for multiple updates to occur without being pushed. The latency should allow reasonable time for previous network traffic to complete without overloading bandwidth unnecessarily.
When an array update occurs, start a timer if not already running for the latency period .
On timer expiry JSON.stringify the array (to take a snapshot), clear the timer running status, and message the client with the JSON text.
A slightly more complicated method to avoid delaying all push operations would be to immediately push single updates unless they occur within a guard period after the most recent push operation. A timer could then push modifications made during the guard period at the end of the guard period.
Broadcasting
The WebSockets API does not directly support broadcasting the same data to multiple clients. Refer to Server Broadcast in ws documentation for an example of sending data to all connected clients using a forEach loop.
Client side listener
In the client-side message listener
document.getElementById("keywordsList").innerHTML = e.data;
would be better as
document.getElementById("keywordsList").textContent = keywordList;
to both present keywords after decoding from JSON and prevent them ever being treated as HTML.
So I finally figured out what I wanted to accomplish. It sounds straight forward after I learned enough and thought about how to structure the back end of my project.
If you have two websockets running and one needs information from the other, you cannot run them side by side. You need to have one encapsulate the other and then call the websocket INSIDE of the other websocket. This can easily cause problems down the road for other projects since now you have one websocket that won't fire until the other is run but for my project it makes perfect sense since it is locally run and needs all the parts working 100 percent in order to be effective. It took me a long time to understand how to structure the code as such.

How to stream data to feed progressbar

I have a NodeJS app that do some computation, and I'd like to fill a progressbar on the client (AngularJS) showing the amount of computing done. For now I do something like this :
Server side :
var compute_percent = 0;
router.post('/compute', function(req, res) {
myTask.compute1(function(result) {
compute_percent = 33;
myTask.compute2(function(result2) {
compute_percent = 66;
myTask.compute3(function(result3) {
compute_percent = 100;
res.json(result3);
});
});
});
}
router.get('/compute_percent', function(req, res) {
res.json(compute_percent);
}
Client Side
angular.module('myApp').controller('myCtrl',
function($scope, $interval, $http) {
$interval(function() {
$http.get('/compute_percent').success(function(result) {
$scope.percent = result});
},
500);
}
});
What I don't like is that I end up doing a lot of request (if I want the progressbar to be accurate) and almost all of the request are useless (the state didn't change server side).
How can I 'inverse' this code, having the server send a message to the listening client that the computing state changed ?
You have 3 possibilities that can be done:
Standard push
By using sockets or anything that can communicate both way, you can do fast information exchange. It will uses many requests for updating the progress bar, but it's pretty fast and can support a tons of request per seconds.
Long pooling
The browser send a request, and the server does not respond immediately, instead it wait for an event to occurred before reporting it by responding. The browser then apply its action and send another request that will be put to wait.
With this technique you will only have update when the server wants. But if you want great accuracy, this will still produce a lot of requests.
Pushlet
The client send only one request, and the server fill it with javascript that will update the progress bar.
The response is stream to keep the connection opened, and javascripts update are sends when needed.
The response will look like:
set_progress(0);
// nothing for X seconds
set_progress(10);
// nothing for X seconds
set_progress(0);
....
Comparing to the others, you still send the same amount of information, but in one request, so it's less heavy.
The easier to implement is the long-pooling I think.

REST API and Real Time client

I want to have the following architecture:
A JSON REST API where real time statistic data is pushed to and stored in a Redis server.
A JSON REST API call where any number of clients (native or web) can receive this data after it has been stored - i.e. in real time.
The first client will just be a web app and I may build a native app later.
I'm wondering if my only option is for the clients to poll the REST API for changes? Ideally, I'd like the server to push updates as they arrive so I don't need to manage this polling.
Is my architecture suitable for what I want to achieve, or am I missing something?
A more efficient way than polling is to use websockets, such as Faye or Socket.IO. You can place an emit event under a data store event to immediately send data that's been stored.
With Socket.IO, you'd do that like this:
var io = require('socket.io').listen(80);
//note that you can listen on HTTP servers
//can also be used with Express applications, etc
//when data is stored, run this
io.sockets.emit('event', {
object: 'that is sent to client'
});
You could then use this to tell the client that there is new data, or you could directly send the newly stored data. Custom events can be defined, such as
io.sockets.emit('data_receive', function (data) {...});
and would be received client side like so:
var socket = io.connect('http://socket.location');
socket.on('data_recieve, function (data) {
//data is whatever sent from server
});
In Faye you'd do something like this:
var http = require('http');
var faye = require('faye');
var bayeux = new faye.NodeAdapter({
mount: '/faye',
timeout: 45
});
bayeux.listen(8000);
Then when data is stored, you'd run:
client.publish('/path', {
data: 'Hello world'
});
Any client that has created a client like so:
var client = new Faye.Client('http://socket:port/path');
client.subscribe('/path', function(data) {
alert('Received data: ' + data.text);
});
Will receive the data.
You have the option of Node.js and the websocket for push and pull in realtime.
To don't manage the queuing you still have the option of MQ.

REST API measuring server-side response times (performance).

I developed some rest APIs based nodejs, I want to test the performance of the APIs. Is any tool can easily count the time of each API call?
Or how to implement measuring of time required for REST API to response on requests.
Here is example of how to make event injection with precise time measuring using express.js.
Add this before your routes:
app.all('*', function(req, res, next) {
var start = process.hrtime();
// event triggers when express is done sending response
res.on('finish', function() {
var hrtime = process.hrtime(start);
var elapsed = parseFloat(hrtime[0] + (hrtime[1] / 1000000).toFixed(3), 10);
console.log(elapsed + 'ms');
});
next();
});
It will save start time of each request, and will trigger finish after response is sent to client.
Thanks for user419127 pointing to 'finish' event
What about a performance measuring tool like Apache JMeter. You can easily use it to simulate a (heavy) load on your server and then measure response times and other performance indicators. It provides multiple graphical representations for this.
This Blog Post shows how you can setup an HTTP based performance test for Web-APIs. I do it this way to test my RESTful webservices.
Keep it simple use the console.time('timerName'); console.timeEnd('timerName') features in Node. 'timerName' is obviously configurable.
Example:
console.time('getCustomers');
console.timeEnd('getCustomers')
Output:
getCustomers: 58ms

Socket.io with Cluster: iterating over all open connections

I'm running Socket.io multi-threaded with the native cluster functionality provided by Node.js v0.6.0 and later (with RedisStore).
For every new change in state, the server iterates over each connection and sends a message if appropriate. Note: this isn't "broadcasting" to all connections, it's comparing server data with data the client sent on connection to decide whether to send the server data to that particular client. Consider this code sample:
io.sockets.clients().forEach(function (socket) {
socket.get('subscription', function (err, message) {
if(message.someProperty === someServerData) {
socket.emit('position', someServerData);
}
});
This worked fine when there was only one process, but now, the client receives a message for each Node process (ie. if there are 8 Node process running, all clients receive the messages 8 times).
I understand why the issue arises, but I'm not sure of a fix. How can I assign a 1-to-1 relation from one process to only on client. Perhaps something using NODE_WORKER_ID of Cluster?
This previous SO question seems somewhat related, although I'm not sure it's helpful.
This seems like a pretty common request. Surely, I must be missing something?
So if I get this straight you need to emit custom events from the server. You can do that by creating your own custom EventEmitter and triggering events on that emitter, for example:
var io = require('socket.io').listen(80);
events = require('events'),
customEventEmitter = new events.EventEmitter();
io.sockets.on('connection', function (socket) {
// here you handle what happens on the 'positionUpdate' event
// which will be triggered by the server later on
eventEmitter.on('positionUpdate', function (data) {
// here you have a function that checks if a condition between
// the socket connected and your data set as a param is met
if (condition(data,socket)) {
// send a message to each connected socket
// if the condition is met
socket.emit('the new position is...');
}
});
});
// sometime in the future the server will emit one or more positionUpdate events
customEventEmitter.emit('positionUpdate', data);
Another solution would be to have those users join the 'AWE150', so only they will receive updates for 'AWE150', like so:
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
if (client_is_interested_in_AWE) { socket.join('AWE150'); }
io.sockets.in('AWE150').emit('new position here');
});
Resources:
http://spiritconsulting.com.ar/fedex/2010/11/events-with-jquery-nodejs-and-socket-io/

Categories

Resources