Simple NodeJs server managed by PM2 for performance optimizations and reversed proxy via nginx. The setup is run in Docker containers. Docker makes it easy to pass env
variables to nginx
Node Js is single threaded and even if we run it on a server with multiple cores, it will still use a single thread(process).
Node has a cluster
module that allows us to take advantage of multi core systems.
It helps to create child process which share same server port but runs on different processes on the CPU core.
Processes are basically scoped containers running our code.
Node comprises of the following:
// update the default thread pool from 4 to 2 process.env.UV_THREADPOOL_SIZE = 2;
This is the basic flow how the code we write flows in the Node land:
JS code ---> JS Engine(V8) ---> NodeJS APIs ---> Node bindings ---> Libuv
Basic non blocking operatiosn are:
Basic blocking operations are:
We can use the Node cluster module to spin up child process and make our code use the multi core cpu, but the management of these child process can get cumbersome, that's why in production its prefered to use PM2. The definition of PM2 from its website
PM2 is a daemon process manager that will help you manage and keep your application online 24/7
We can configure PM2 either using cli arguments
or the config
file, and in this repo we have used the config file ecosystem.config
to configure our PM2. Here is our basic configuration:
4 \* 2 = 8
.module.exports = { apps: [ { name: "node-be", script: "./server-pm2.js", exec_mode: "cluster", instances: "max", log_type: "json", log_file: "./logs/log.json", out_file: "./logs/out.json", error_file: "./logs/error.json", env: { PORT: 3000, SERVER_NO: "Server 1", }, }, { name: "node-be-2", script: "./server-pm2.js", .... env: { PORT: 3001, SERVER_NO: "Server 2", }, }, ], };
cli
here is an example:
pm2 start server.js -i 0
Nginx is a web server that can also be used as a reverse proxy, load balancer, mail proxy and HTTP cache. We have used it for reverse proxy and load balancing.
template
setup of NGINX bcs it works well with environment variables and let us substitute the values when the docker image for NGINX runs.default.conf.template
file that holds the server
directive that will be overriding the default one.// default.conf.template file server { listen ${NGINX_PORT}; server_name ${NGINX_HOST}; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_pass http://${SERVER_HOST}:${SERVER_PORT}/; proxy_http_version 1.1; } }
upstream
to work with nginx and docker locally we have to switch from using
localhost
in upstream to the local ip instead 172.18.0.1
// instead of upstream nodeapi { server localhost:${SERVER_PORT}; server localhost:${SERVER_PORT2}; } // to using local ip upstream nodeapi { server 172.18.0.1:${SERVER_PORT}; server 172.18.0.1:${SERVER_PORT2}; }
For local development make sure the following things are setup:
npm install
for installing the dependencies..env
file provided feel free to update the values. But make sure the PORT values match the PM2 config.docker composr up --build ---> to re build the image docker compose up --> if image exists docker exec -it nginx-server sh ---> to checks thr logs in docker container. Here `nginx-server` is the name of the container.