- Hands-On Data Structures and Algorithms with JavaScript
- Kashyap Mukkamala
- 930字
- 2021-06-30 19:12:14
Creating a chat endpoint
Now that we have the server up and running, we can create an in-memory chat endpoint, which would accept a message from two users and forward it to its intended recipient using a queue while retaining the order.
Before we add the logic, we will need to do some groundwork to set up the application in a modular way. First, let's include the body-parser and use it in an express middleware so that we can access the body of requests easily. So, the updated index.js file looks as follows:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.get('/', function (req, res) {
res.status(200).send('OK!')
});
app.listen(3000, function () {
console.log('Chat Application listening on port 3000!')
});
Now, to add the endpoint for the message, we can create a new file called messages.js under the routes folder to which we can add the basic post request:
var express = require('express');
var router = express.Router();
router.route('/')
.post(function(req, res) {
res.send(`Message received from: ${req.body.from} to ${req.body.to} with message ${req.body.message}`);
});
module.exports = router;
Then, we can inject it in our index.js and make it a part of our application:
var message = require('./routes/messages');
...
...
...
app.use('/message', message);
Now, to test this, we can start our server and post a message to localhost:3000/message using Postman; then we can see the response posted, as follows:

Now, we can go ahead and start adding the logic to send messages between two users. We are going to abstract, mock, and simplify the chat part of the application and focus more on queue applications in such complex applications.
The workflow itself is relatively straightforward: user A sends a message to user B, which our server tries to forward to user B. If it goes through without any issue, then everything is good, and the message is delivered to user B; but if it fails, then we invoke our FailureProtocol(), which retries to send the last failed message per-conversation. For simplicity, we will assume that there is only one channel right now, that is, between user A and user B.
The production counterpart of this would be capable of handling multiple channels simultaneously by creating a new FailureProtocol() handler for a particular channel when a message fails on a channel and would have the flexibility of deferring the job over to multiple threads.
Let's now mock out the sendMessage() and getUniqueFailureQueue() methods in a file called messaging-utils.js which will be our wrapper so that we can move them into their own module, as their internal workings are not really important to understand queues in this scenario:
var PriorityQueue = require('./priority-queue');
var Utils = (()=> {
class Utils {
constructor() {
}
getUniqueFailureQueue(from, to) {
// use from and to here to determine
// if a failure queue already
// exists or create a new one
return new PriorityQueue();
}
sendMessage(message) {
return new Promise(function(resolve, reject) {
// randomize successes and failure of message being
sent
if(Math.random() < 0.1) {
resolve(message)
} else {
reject(message);
}
});
}
}
return Utils;
})();
module.exports = Utils;
Now, when we receive a new message, we try to send it to the intended end user:
var express = require('express');
var router = express.Router();
var Utils = require('../utils/messaging-utils');
const msgUtils = new Utils();
router.route('/')
.post(function(req, res) {
const message = req.body.message;
let failedMessageQueue;
// try to send the message
msgUtils.sendMessage(req.body)
.then(function() {
res.send(`Message received from: ${req.body.from} to ${req.body.to} with message ${req.body.message}`);
}, function() {
failedMessageQueue =
msgUtils.getUniqueFailureQueue(req.body.from,
req.body.to);
failedMessageQueue.add(message);
// trigger failure protocol
triggerFailureProtocol();
});
If the message is sent successfully, we will need to immediately acknowledge that and send a success message—otherwise, we will get a unique failedMessageQueue between the two users—and then add the message to it, which is then followed by triggering the failure protocol.
A failure protocol can mean anything to different applications. While some applications choose to just show a failed message, applications such as ours will retry to send the message until it is sent successfully:
function triggerFailureProtocol() {
var msg = failedMessageQueue.front();
msgUtils.sendMessage(msg)
.then(function() {
failedMessageQueue.remove();
res.send('OK!');
}, function(msg) {
//retry failure protocol
triggerFailureProtocol();
});
}
We can use the methods available in our Queue to pick the top message and then try to send it. If successful in doing so, then remove it; otherwise, retry. As you can see, using queues greatly simplifies and abstracts the logic of the actual queuing of failed messages and what is even better is that you can upgrade and enhance the queue at any time without having to think twice about what other components would get affected by that change.
Now that we have the API call ready to parse the incoming request, send it to the intended recipient and trigger our custom failure protocol. When we combine all of this logic together, we have the following:
var express = require('express');
var router = express.Router();
var Utils = require('../utils/messaging-utils');
const msgUtils = new Utils();
router.route('/')
.post(function(req, res) {
const message = req.body.message;
let failedMessageQueue;
// try to send the message
msgUtils.sendMessage(req.body)
.then(function() {
console.log("Sent Successfully : " + message);
res.send(`Message received from: ${req.body.from} to ${req.body.to} with message ${req.body.message}`);
}, function(msg) {
console.log('Failed to send: ' + message);
failedMessageQueue =
msgUtils.getUniqueFailureQueue(req.body.from,
req.body.to);
failedMessageQueue.add(message);
// trigger failure protocol
triggerFailureProtocol();
});
function triggerFailureProtocol() {
var msg = failedMessageQueue.front();
msgUtils.sendMessage(msg)
.then(function() {
failedMessageQueue.remove();
res.send('OK!');
}, function(msg) {
//retry failure protocol
triggerFailureProtocol();
});
}
});
module.exports = router;
- Getting started with Google Guava
- Moodle Administration Essentials
- STM32F0實戰:基于HAL庫開發
- Python數據可視化之Matplotlib與Pyecharts實戰
- 數據結構與算法分析(C++語言版)
- Salesforce Reporting and Dashboards
- 領域驅動設計:軟件核心復雜性應對之道(修訂版)
- Test-Driven Development with Django
- Android驅動開發權威指南
- ASP.NET程序開發范例寶典
- BeagleBone Robotic Projects(Second Edition)
- 基于GPU加速的計算機視覺編程:使用OpenCV和CUDA實時處理復雜圖像數據
- R的極客理想:量化投資篇
- C/C++代碼調試的藝術(第2版)
- Node.js實戰:分布式系統中的后端服務開發