MEAN Stack Project Performance Improvements

M - MongoDB
E - Express
A - Angular
N - NodeJS
We have implemented the followings things to improve the performance of MEAN stack
server.

    1. ELB implementation
    2. NodeJS Clustering
    3. Amazon S3
    4. Mongo replication
    5. Microservice implementation


1. ELB implementation

When it comes to increasing the performance of websites and web services, there are only a couple of options: increase the efficiency of the code, or scale up the server infrastructure. Let us discuss about, how we can scale the nodejs server infrastructure using ELB, which stands for Elastic Load Balancing, which automatically distributes the incoming application traffic across a group of backend servers. The load balancer routes the incoming requests to the second server when the first server is busy with processing other requests, so that the end user will not feels any delay in getting the response. There are 3 types of load balancers:

    1. Application Load Balancer
    2. Network Load Balancer
    3. Classic Load Balancer

Application Load Balancer is best suited for load balancing of HTTP and
HTTPS traffic and provides advanced request routing targeted at the delivery of modern application architectures, including microservices and containers. Operating at the individual request level (Layer 7), Application Load Balancer routes traffic to targets within Amazon Virtual Private Cloud (Amazon VPC) based on the content of the request. The socket forwarding is supported only in the application load balancer.  So we have used the amazon AWS application load balancer to scale the server architecture.

The decision on how many servers to be used in the load balancer, depends
upon the traffic expected in the server, the number of concurrent requests the
server need to handle. And also it depends upon the system configurations in
which the server is hosted like number of cores, memory etc. We need to change
some web server (egs: nginx or apache) configurations as described here.

To discover the availability of your EC2 instances, a load balancer
periodically sends pings, attempts connections, or sends requests to test the EC2
instances. These tests are called health checks. The status of the instances that
are healthy atthe time of the health check is InService. The status of any
instances that are unhealthy at the time of the health check is OutOfService. The
load balancer performshealth checks on all registered instances, whether the
instance is in a healthy state or an unhealthy state.

The load balancer routes requests only to the healthy instances. When the
load balancer determines that an instance is unhealthy, it stops routing requests
to that instance. The load balancer resumes routing requests to the instance when
it has been restored to a healthy state.

The load balancer checks the health of the registered instances using either
the default health check configuration provided by Elastic Load Balancing or a
health check configuration that the developer configure. In the second case, the
developer has to give one health checking url ({main domain}/health) to the admin
team(who is doing the ELB implementation), which serves the health status of the
instance.

The health url should be an api configured in nodejs program, which
connects to the database and fetches some lightweight data to make sure that
the instance is properly working ie, it ensures the health of web server (Nginx),
server(Node Js instance) and the database (Mongodb). If this api could respond
with the http status 200 within a particular time (Say 2 sec, which we can set in
the load balancer configuration), the instance marks as healthy otherwise
unhealthy. The load balancer periodically (Say 10 sec, which we can set) checks
the health status of each instances through this health url, to mark it healthy or
unhealthy. So when the next request comes, the load balancer routes the request
to a healthy server instance which is available.

We are using the LoopbackJs - a Node Js framework, and we can put the
following code in the file called server/boot/root.js (In loopback framework folder
structure) to create the health checking api url.

Code:
    
        module.exports = function(server) {
            var router = server.loopback.Router(); // router module
             router.get('/health', function(req, res) {
                  server.models.ServerHealthStatus.findOne({where: {status: 1}, fields: "status"}, function(err, healthStatus) {
                      var response = "";
                      if (err) { //failure
                          res.status(400).send(response).end();
                      } else {
                          if (healthStatus) { // success
                              response += "Health: OK";
                              res.send(response); // http status: 200
                          } else { //failure
                              res.status(400).send(response).end();
                          }
                      }            
                  });
            });
          };
    

In this code, ServerHealthStatus is a model created in loopback which is
mapped to the mongo collection (Similar to table in mysql) - ServerHealthStatus,
which is having the following one document (Similar to row in mysql):

    
        {
            "_id" : ObjectId("5b2b7u5d1726126da11b1f98"),
            "status" : "1"
        }
    

The model query tries to fetch this document, and sends the response -
either 200 or 400 depends upon the query result. If the Node JS service is busy
with processing other requests, it may fails to retrieve data or it may take more
time to respond, which may exceed the time that we have configured in the
load balancer timeout.

Since our server is distributed over multiple instances, first we need to make
sure that, the resources like images or files (user profile images, documents) are
located in a place, which is accessible to all instances. Earlier we may be using
the local file system for resource storage. Since the load balancer is implemented,
there are multiple instances which needs these contents to be accessible. So in
order to solve this problem, it must be saved in a common place like Amazon S3.

After hosting the server, we need to do the load testing with some automated
tools to make sure that the server can handle the specified number of concurrent
requests and handles the traffic. If it fails, we need to increase the server
configurations or the number of server instances until it meets. We need to
periodically tracks the server for the cpu and memory usage while doing the load
testing. After implementing the load balancer, we need to do the failover test as
well. ie We need to manually down one instance and check whether other
instances serves the client request without any delay. And also we need to do the
load testing by putting the load specified in the project requirement(say 10K
concurrent requests).  

2. NodeJS Clustering

A single instance of Node.js runs in a single thread, To take advantage of
multi-core systems, the user will sometimes want to launch a cluster of Node.js
processes to handle the load. The cluster module allows easy creation of child
processes that all share server ports.

Code:

    
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  // Workers can share any TCP connection
  // In this case it is an HTTP server
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}
    

3. Amazon S3

We are using Amazon S3 for the resource storage. Earlier we were using
the local file system for resource storage. Since the load balancer is implemented,
there are multiple instances which needs these contents to be accessible, so it
must be saved in a common place.

 4. Mongo Replication

A replica set is a cluster of  MongoDB database servers that implements
master-slave (primary-secondary) replication. Replica sets also failover
automatically, so if one of the members becomes unavailable, a new primary
host is elected and your data is still accessible. We have used 3 mongo servers.
The entire process is clearly described here:


We will specify the server domain names or ips of the mongo servers in the
database connection url like this:

    
mongodb://userName:password@mongo1,mongo2,mongo3/databaseName?replicaSet=rs0&connectTimeoutMS=180000&socketTimeoutMS=180000
    

Where mongo1, mongo2 & mongo3 are the mongo server names (wh-ich uses internal domain names, since all server exists inside one aws account ie admins can give user defined names mapped to ip address which can be used instead of ip addresses) which is pointing to different ip addresses.

In the loopback structure, need to change the connection url in the  datasources.json
file in the server folder like this:
    
{
"db": {
    "name": "db",
    "connector": "mongodb",
    "url": "mongodb://userName:password@mongo1,mongo2,mongo3/databaseName?replicaSet=rs0&connectTimeoutMS=180000&socketTimeoutMS=180000"
    }
}
    


 5. Microservice implementation
If there are some complex operations, which needs more system resources (CPU, RAM) for a considerable amount of time,, can be implemented as a micro service,
so that the load can be reduced in the main server. Ie, we will implement this functionality
in an independent server which can be accessible via an API url. The main server will
trigger this api url for doing this task done.

For example: Sending push notification functionality to mobile apps

Since the push notification sending functionality is a time consuming process,
during this process, the server load will be high which may results in queueing
other coming requests. In order to solve this, we have separated the push
notification functionality to an independent server as a microservice.

After implementing these all we can monitor the resource usage of all the
servers while performing the functionalities especially the complex functionalities.










No comments:

Post a Comment

Technologies

1. Nodejs
  • Express
  • Loopback
  • SailsJs
2. Angular
  • 2
  • 4
  • 5
  • 8
3. JQuery
4. PHP
  • LAMP
  • Codeigniter
  • Lumen
5. Database
  • MySQL
  • PostgreSQL
  • MongoDB
6. Artificial Intelligence
  • IBM Watson API
7. Amazon
  • S3
  • EC2
  • SES
  • ELB
8. REST & SOAP API integrations
9. Push notifications
  • FCM - Android
  • APNS - IOS
10. RETS(Real estate domain) servers
11. Standard Implementations
  • 508
  • LTI