Messages (beyond an e-mail, a SMS…) are streamed data whose processing (filtering, aggregating…) comes from real-time needs.
Basically, 3 strategies exist with Node.js.
- HTTP-HTTPS Messaging relies on Server-sent Events. Client subscribes to server data from polling principle managed by server.
- WebSockets
- Message brokers are middleware in which messages are published and consumed by subscribers. Publishers and subscribers are broker “clients”. The key advantage is load balancing facilities provided by middleware.
Upon HTTP-HTTPS, clients that have to timely get data may generate too many requests with a risk of overloading servers. Server-sent Events allow clients to subscribe to these data (
onmessage
handler function below), which are then pushed by servers. Clients no longer have to generate HTTP-HTTPS requests.Example (server-side) Server-sent.Node.js.ts.zip
const server = http.createServer((request, response) => { if (request.url === "/") { // 'index.html' served... response.writeHead(200, {"Content-Type": "text/html; charset=utf-8"}); const __dirname = path.dirname(fileURLToPath(import.meta.url)); fs.readFile(path.resolve(__dirname, '..') + '/index.html', null, (error, data) => { if (error) { response.writeHead(404); response.write("Whoops! 'index.html' not found..."); } else response.write(data); // Content of 'index.html'... response.end(); }); } else if (request.url === '/Server_sent_data') { // Client asks for dealing with 'Server-sent' data... response.statusCode = 200; response.setHeader("Access-Control-Allow-Origin", "*"); // CORS response.setHeader("Cache-Control", "no-cache"); // Client and server agree to keep the connection for subsequent requests or responses open: response.setHeader("connection", "keep-alive"); response.setHeader("Content-Type", "text/event-stream"); const data = JSON.stringify({integer: Math.floor(Math.random() * 10)}); // [0 - 10[ // The event-stream format must be respected (https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format): setTimeout(() => response.write(`id: ${(new Date()).toLocaleTimeString()}\ndata: ${data}\n\n`), 2500); } }); server.listen(process.env['PORT'] || 8080);
Example (client-side) Server-sent.Node.js.ts.zip
window.onload = () => { const event_source = new EventSource("http://127.0.0.1:8080/Server_sent_data"); window.console.assert(event_source.readyState === event_source.CONNECTING); const message_data = window.document.getElementById("message_data"); // Stop listening server: message_data.addEventListener('click', () => { event_source.close(); message_data.textContent = "Server-sent data stopped..."; }); event_source.onopen = (event) => window.console.assert(event_source.readyState === event_source.OPEN); event_source.onmessage = (event) => { const {integer} = window.JSON.parse(event.data); message_data.textContent = integer + " just received... 'click' to stop or 'reload' for new data)"; }; // Other stuff here... };
Exercise
- Implement a clock…
The native WebSockets API in the browser (example) allows the connection and exchange with a WebSockets server. To have a WebSockets server on the top of Node.js calls for appropriate libraries like ws or socket.io (socket.io uses WebSockets as transportation facility; however, it is incompatible with the WebSockets API in the browser).
Example (server-side) WebSockets.ts.zip
import * as WebSocket from "ws"; const Launch_WebSockets_server = (configuration: Object = {host: 'localhost', path: '/FranckBarbier', port: 1963}) => { const server = new WebSocket.Server(configuration); server.on('connection', (ws: WebSocket) => { console.log('OnOpen... status: ', ws.readyState === 1 ? "OPEN" : "Not OPEN"); ws.on('message', (event: WebSocket.MessageEvent) => { console.log('Message from client: %s', event); }); ws.send("{\"Connected\": \"Yes\"}"); }); server.on('close', (event: WebSocket.CloseEvent) => { console.log('OnClose: ', event ? event.reason : "Unknown reason"); }); server.on('error', (event: WebSocket.ErrorEvent) => { console.log('onError: ', event ? event.error : "Unknown error"); }); setTimeout((information) => { console.log(`${information}`); server.close(); }, 10000, 'WebSockets server is stopping...'); }; export default Launch_WebSockets_server;
Example (launch server and client in browser) WebSockets.ts.zip
// import Launch_WebSockets_server from "./Server"; // Compilation... import Launch_WebSockets_server from "./Server.js"; // Execution... Launch_WebSockets_server({host: 'localhost', path: '/FranckBarbier', port: 1963}); // Just create a Web server to have the WebSockets client in the browser: const port = process.env['PORT'] || 8080; const server = http.createServer((request, response) => { response.writeHead(200, {"Content-Type": "text/html; charset=utf-8"}); const __dirname = path.dirname(fileURLToPath(import.meta.url)); // console.info("Location of 'index.html': " + path.resolve(__dirname, '..')); fs.readFile(path.resolve(__dirname, '..') + '/index.html', null, function (error, data) { if (error) { response.writeHead(404); response.write("Whoops! 'index.html' not found..."); } else response.write(data); response.end(); }); }).listen(port); console.log("Web server ready to accept requests on port %d", port); setTimeout((information) => { // 'open' is a utility to launch the client in the 'index.html' file: open("http://localhost:" + port).then(() => { console.log(`${information}` + " Browser just opened..."); }); }, 1000, 'WebSockets server started 1 sec. ago...');
The Mediator design pattern prompts us to decouple communication ends in terms of messaging. In this logic, brokers are proxys to supply communication facilities like load balancing, security (crypting…), fault tolerance…
Apache Kafka, Mosquitto, or RabbitMQ are popular message brokers. Messaging protocols are varied, but MQTT is an OASIS worldwide standard and, as such, widely supported.
Messaging broker clients require appropriate libraries like KafkaJS (Apache Kafka), MQTT.js (Mosquitto), or amqp.js (RabbitMQ) to allow clients' connection and exchange with messaging brokers.
Intuitive messaging versus broker-based messaging (right-hand side)