[Node.js] Web Architecture Design
1) Traditional Web Application Processing Model - Multi-Threaded Request-Response
Any Web Application developed without NodeJS typically follows the "Multi-Threaded Request-Response model" (or just "Request/Response Model"). This differs from the NodeJS way in that it uses multiple threads to handle concurrent client requests.Under the hood, server waits in infinite loop and creates one thread per client request. If the server gets many client requests that require long blocking I/O operations, then more threads become 'busy'. As a result, the remaining clients requests have to wait longer due to core-thread resources.
1.1) Detailed Flow
- Ahead of time:
- Web Server is in infinite loop and waiting for Client Incoming Requests.
- Web Server internally maintains a Limited Thread pool to provide services to Client Requests.
- Once threads free up in thread pool, server pick up those threads and assign them to remaining client requests.
- Clients send request to Web Server
- Web Server receives request.
- Web Server picks a thread from thread pool to process Client Request, including blocking I/O operations.
- This thread finishes processing and sends a response back to Client.
1.2) Drawbacks
- When concurrent client requests increase, it uses more threads, which then eat up more memory (highly dependent on memory)
- Under high traffic, Client's requests need to wait for available threads to process their requests.
- Wastes time in processing blocking IO tasks.
- Cannot handle as many concurrent client requests as NodeJS can.
2) NodeJS Architecture - Single-Threaded Event Loop
NodeJS platform does not follow Request/Response Multi-Threaded stateless model. Instead, it follows Single Threaded with Event Loop. The main reason is it scales well as the # of concurrent client requests increase.
2.1) Detailed Flow
- Ahead of time:
- NodeJS Web Server internally has a infinite loop, called "single-threaded event loop". This loop checks any Client Requests placed in the Event Queue.
- NodeJS Web Server internally maintains a limited thread pool, via libuv, to process blocking operations.
- Clients send request to Web Server
- If client request is processed with no blocking operations, then process everything, prepare response and send back to client.
- Note that asynchronous I/O operations are non-blocking, such as DB requests.
- If client request is processed with blocking operations, eg. any CPU-intensive tasks or any I/O operations that are not asynchronous, then it will assign available threads from thread pool to process it, then send response to client when it is processed.
3) Comparisons
3.1) Concurrent Client Requests/Connections
As for the traditional multi-threaded request-response model, if we assume that each thread takes up 2MB of memory, running on a system with 8GB of RAM puts us at a theoretical maximum of 4000 concurrent connections, plus the cost of context-switching between threads.
But for NodeJS' single-threaded event loop model, NodeJS achieves scalability of over 1M concurrent connections and over 600K concurrent websocket connections (based on http://blog.caustik.com/2012/08/19/node-js-w1m-concurrent-connections and https://blog.jayway.com/2015/04/13/600k-concurrent-websocket-connections-on-aws-using-node-js/)
3.2) Scaling with hardware
During high traffic, single-threaded NodeJS may not be able to fully utilize all of the cores, whereas the traditional model scales with cores and RAM - as long as you have enough memory and cores, you shouldn't face scalability problems.
3.3) Code Maintenance
Multi-Threads are not easy to maintain. However, a single-threaded event loop is not easy as well because everything relies on 1 thread (the single-threaded event loop). Heavy computations can choke up the event loop and unhandled errors can effectively crash the entire program.
3.4) CPU-intensive tasks
NodeJS' model is not suitable for CPU-intensive tasks, because it is single-threaded. CPU-intensive tasks does not have to complex algorithms. Sorting, JSON parsing or traversing an array can be considered CPU-intensive on medium-sized data structures; especially when you are using only 1 thread to handle thousands of client requests.
4) Hybrid
In short, by using NgInx, Apache2 or any load balancing mechanism, you can spawn multiple processes of the Web Server and distribute it across them.
Comments
Post a Comment