Real-Time Server Monitoring with NestJS and WebSockets Dashboard
Written on
Introduction to Real-Time Insights
In the realm of web development and system management, acquiring immediate insights into server performance can greatly improve operational efficiency and responsiveness. With the rise of real-time communication technologies, developers are now equipped to implement dynamic monitoring solutions that provide instant visibility into system health. This guide aims to explore the capabilities of WebSockets by demonstrating how to build a server metrics dashboard using NestJS. This project showcases a practical and advanced use case that highlights the power of real-time web communication.
As we delve deeper into this topic, our attention will focus not only on how to capture and display essential server metrics—such as CPU and memory usage—but also on the technology that enables this swift data delivery. Our objective is to foster a comprehensive understanding of WebSockets through a hands-on approach.
Understanding WebSockets
WebSockets differ from the conventional request-response model employed in HTTP. They establish a persistent, bi-directional communication channel between the client and server, allowing data to be sent and received at any time without polling. This enables instantaneous data transfer, making WebSockets ideal for real-time applications.
Operating over a single, long-lived connection, WebSockets significantly reduce latency and overhead compared to multiple HTTP requests. This characteristic makes them particularly suited for monitoring applications, where timely data updates are crucial.
The WebSocket connection begins with a standard HTTP request from the client to the server, which includes a specific header (Upgrade: websocket) indicating the desire to establish a WebSocket connection. If the server supports WebSockets, it responds with an HTTP 101 status code (Switching Protocols), along with its own Upgrade: websocket header to confirm the protocol switch. Following this handshake, the connection is upgraded from HTTP to a WebSocket connection, creating a full-duplex communication channel that remains open for the connection's duration.
Setting Up with NestJS
NestJS is a progressive Node.js framework that provides a scalable architecture with built-in support for WebSockets, making it an ideal choice for our server metrics dashboard. Its module-based design promotes organized code structure and allows for easy integration of various components, including WebSocket gateways that we will utilize for real-time communication.
Project Overview
Our goal is to develop a server metrics dashboard to display real-time CPU and memory usage. We will accomplish this by:
- Setting up a NestJS project: Initialize a new NestJS project and configure it for our dashboard.
- Implementing WebSocket communication: Create a WebSocket gateway within our NestJS application to manage real-time data transmission.
- Capturing server metrics: Use Node.js libraries to gather CPU and memory usage data.
- Serving the dashboard: Configure NestJS to serve an HTML dashboard that connects to our WebSocket server and displays metrics in real time.
Step 1: Initializing the NestJS Project
nest new server-metrics
cd server-metrics
Step 2: Installing Dependencies
npm install @nestjs/websockets @nestjs/platform-ws os-utils @nestjs/serve-static
Step 3: Creating the Metrics Gateway
nest g gateway metrics
The above command creates a metrics gateway that will serve as the entry point for our WebSockets. It also adds the Metrics Gateway to the providers in AppModule, which is necessary for initiating WebSocket communication in NestJS.
Step 4: Implementing Metrics Events
import { WebSocketGateway, OnGatewayInit, WebSocketServer } from '@nestjs/websockets';
import { Server } from 'ws';
import * as osUtils from 'os-utils';
@WebSocketGateway()
export class MetricsGateway implements OnGatewayInit {
@WebSocketServer() server: Server;
public async afterInit() {
setInterval(async () => {
const cpuUsage: number = await new Promise(resolve => osUtils.cpuUsage(resolve));
const totalMem = osUtils.totalmem();
const freeMem = osUtils.freemem();
const response = {
cpuUsage: (cpuUsage * 100).toFixed(2),
freeMemory: (freeMem).toFixed(2),
totalMemory: (totalMem).toFixed(2),
}
this.server.clients.forEach(
client => client.send(JSON.stringify(response)))
}, 1000);
}
}
- @WebSocketGateway(): This decorator marks the class as a WebSocket gateway. The empty parentheses indicate that it uses default settings, but customization is possible.
- server: Server;: This property holds the WebSocket server instance.
- afterInit: This method, part of the OnGatewayInit interface, executes after the gateway is initialized. It sets up a repeating interval that triggers every second. The block within setInterval calculates CPU usage using osUtils.cpuUsage, which returns a value between 0 and 1. This value is then multiplied by 100 and formatted to two decimal places. It also retrieves total and free memory using osUtils.totalmem() and osUtils.freemem(). A response object is constructed with these metrics, which are then sent to all connected clients as a stringified JSON object, allowing them to receive real-time updates on CPU and memory usage.
Step 5: Creating the Client
Create a public directory and within it, a file named dashboard.html. Populate it with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Server Metrics Dashboard</title>
<style>
body {
font-family: Arial, sans-serif;}
.metrics-container {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 50px;
width: 300px;
margin-left: auto;
margin-right: auto;
}
.metric {
margin: 10px;
padding: 20px;
background-color: #f0f0f0;
border-radius: 8px;
width: 300px;
}
</style>
</head>
<body>
<div class="metrics-container">
<div class="metric" id="cpuUsage">CPU Usage: Loading...</div>
<div class="metric" id="freeMemory">Free Memory: Loading...</div>
<div class="metric" id="totalMemory">Total Memory: Loading...</div>
</div>
<script>
const socket = new WebSocket('ws://localhost:3000');
socket.onerror = function(error) {
console.error('WebSocket Error: ', error);};
socket.onopen = function(event) {
console.log("Connection opened");};
socket.onmessage = function(event) {
const metrics = JSON.parse(event.data);
document.getElementById('cpuUsage').textContent = CPU Usage: ${metrics.cpuUsage}%;
document.getElementById('freeMemory').textContent = Free Memory: ${metrics.freeMemory} MB;
document.getElementById('totalMemory').textContent = Total Memory: ${metrics.totalMemory} MB;
};
socket.onclose = function(event) {
console.log("Connection closed");};
</script>
</body>
</html>
In this script, we subscribe to the WebSocket and update the text content based on the received values.
Step 6: Serving the Dashboard File
Inside the app module, we will serve static files by using the ServeStaticModule and adding the following code to the import array:
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'public'),
})
Step 7: Running the Server
npm run start:dev
Once the server starts, the dashboard will be accessible at http://localhost:3000/dashboard.html (make sure to update the port if necessary).
Conclusion and Future Directions
By following these steps, you have successfully created a real-time server metrics dashboard utilizing NestJS and WebSockets. This project not only illustrates the practical application of WebSockets for real-time data streaming but also provides a foundation for developing more complex monitoring solutions.
As you enhance your dashboard, consider integrating additional metrics, implementing authentication for WebSocket connections, or exploring methods to optimize data transmission. The opportunities are extensive, and the tools provided by NestJS and WebSockets offer a powerful platform for real-time web applications.
This exploration of real-time communication highlights the transformative potential of WebSockets, offering a glimpse into the dynamic and interactive future of web development.