Most real-world applications we ship to consumers or enterprises are multi-year projects. In the cloud era, newer technologies (programming languages, runtimes, frameworks) are created faster than ever. While most of them fail to get any traction, once in a while a technology becomes popular because it solves a problem or set of problems extremely well.
Now in such an era, if you make a large investment for a multi-year project on a PaaS that only supports one technology and some other technology comes along that happens to solve your problem better, then you are stuck. You have unintentionally become a victim of vendor lock-in. The heart of your problem is that your PaaS, and hence your app, was not future-proofed to begin with.
To future-proof your long term project, you should:
- 1. Use a polyglot PaaS like Cloud Foundry that supports a great mix of both mature technologies and upcoming technologies.
- 2. Learn about newer and popular technologies like Node.js to:
See if they can replace part of your current app (i.e., convert it to a polyglot app).
Write future apps for your company in newer technologies using the same PaaS that you are already familiar with.
The remainder of this blog is about the latter option–learning newer and popular technologies, in this case Node.js, to help future-proof your app.
Things to note before you read:
- We will also discuss when not to use Node.js and other similar languages towards the end of the blog.
What is Node.js?
Some useful definitions:
We encourage you to go through Google’s Chrome comic book to learn more about this. Three relevant pages from that book are shown below.
- Asynchronous I/O and Evented Support (C/C++): In order to write a fast and scalable server application, we typically end up writing it in a multi-threaded fashion. While you can build great multi-threaded apps in many languages, it usually requires a lot of expertise to build them correctly. On the other hand, these libraries (along with Chrome’s V8 engine) provide a different architecture that hides the complexities of multi-threaded apps while getting the same or better benefits.
- Let’s compare classic multi-threaded server with an evented, non-blocking I/O server:
- An example multi-threaded HTTP server using blocking I/O
- The above diagram depicts a simplified multi-threaded server. There are four users logging into the multi-threaded server. A couple of the users are hitting refresh buttons causing it to use lot of threads. When a request comes in, one of the threads in the thread pool performs that operation, say, a blocking I/O operation. This triggers the OS to perform context switching and run other threads in the thread pool. And after some time, when the I/O is finished, the OS context switches back to the earlier thread to return the result.
- Architecture Summary: Multi-threaded servers supporting a synchronous, blocking I/O model provide a simpler way of performing I/O. But to handle a heavy load, multi-threaded servers end up using more threads because of the direct association to connections. Supporting more threads causes more memory and higher CPU usage due to more context switching among threads.
- For more details, we recommend going through Benjamin Erb’s thesis paper here: http://berb.github.com/diploma-thesis/.
- Event-driven, non-blocking I/O (Node.js server):The above diagram depicts how Node.js server works. At a high level, Node.js server has two parts to it:
At the front, you have Chrome V8 engine (single threaded), event loop and other C/C++ libraries that run your JS code and listen to HTTP/TCP requests.
- And at the back of the server, you have libuv (includes libio) and other C/C++ libraries that provide asynchronous I/O.
- Whenever a request is made from a browser, mobile device, etc., the main thread running in the V8 engine checks if it is an I/O. if it is an I/O then it immediately delegates that to the backside (kernel level) of the server where one of the threads in the POSIX thread pool actually makes async I/O. Because the main thread is now free, it starts accepting new requests/events.
- And at some point when the response comes back from a database or file system, the backend piece generates an event indicating that we have a result from I/O. And when V8 becomes free from what it is currently doing (remember it is single-threaded), it takes the result and returns it to the client.
- Architecture Summary: This architecture utilizes an event loop (main thread) at the front and performs asynchronous I/O at the kernel level. By not directly associating connections and threads, this model needs only a main event loop thread and many fewer (kernel) threads to perform I/O. Because there are fewer threads and consequently less context-switching, it uses less memory and also less CPU.
What are the benefits of Node.js?
- Savings in I/O cost (i.e., high performance): Because of the architecture, Node.js provides high performance like Nginx server as shown below. (As a side note: Nginx uses evented, non-blocking architecture, where as Apache uses multi-threaded architecture. Nginx doesn’t use Node.js, this is just an architecture comparison).
- Savings in Memory: Again, because of the architecture, Node.js uses relatively very little memory much like Nginx server as shown below.
- Thousands of libraries: High performance and a familiar language is great, but you really need libraries to get started. Although Node.js is relatively new, it already has nearly 11,000 libraries.
- Second most popular watched project on Github: A large ecosystem of developers means better libraries and frameworks.
When to use Node.js:
Use Node.js to:
- Build a (soft) real-time social app like Twitter or a chat app.
- Build high-performance, high I/O, TCP apps like proxy servers, PaaS, databases, etc.
- Build backend logging and processing apps.
- Build great CLI apps similar to vmc-tool, and build tools such as ant or Make.
- Add a RESTful API-based web server in front of an application server.
When NOT to use Node.js:
Node.js is not suitable for every application:
- Mission-critical (hard) real-time apps like heart monitoring apps or those that are CPU-intensive.
- For simple CRUD apps that don’t have any real-time or high-performance needs, Node.js does not provide much of an advantage over other languages.
- Enterprise apps that might need some specific libraries for which there may not be a Node.js library yet. (However, you could build a polyglot app that uses Java in conjunction to Node.js to help with libraries.)
What are the drawbacks of Node.js:
Most of the drawbacks are because Node.js itself is relatively new:
- Node.js libraries are developed actively with a high rate of change. There are newer versions of libraries literally every month. This can cause version issues and instabilities. Npm shrinkwrap and package.json were introduced a while back to set up standards, but the issue still exists.
- Still many libraries, such as the SAML auth library which is required for enterprise apps, are not available yet.
- Asynchronous and event-driven code inherently adds more complexity to the code versus a synchronous code.
What are other similar and newer languages I should be aware of:
- Erlang: Erlang is a programming language used to build massively scalable soft real-time systems with requirements on high availability. Some of its uses are in telecoms, banking, e-commerce, computer telephony and instant messaging. Erlang’s runtime system has built-in support for concurrency, distribution and fault tolerance.
- Twisted: Twisted is an event-driven networking engine written in Python and licensed under the open source.
- EventMachine: EventMachine is an event-driven I/O and lightweight concurrency library for Ruby. It provides event-driven I/O using the Reactor pattern.
- Scala: Scala is a general purpose programming language designed to express common programming patterns in a concise, elegant and type-safe way.
- Go: Go is an open source programming environment that makes it easy to build simple, reliable and efficient software.
- Raja Rao DV (@rajaraodv – Developer Advocate, Cloud Foundry, (Node.js))