Here are 10 performance rules we follow when using Node.js:
1. Avoid using synchronous code
In terms of design, Node.js is single-threaded. To allow a single thread to handle many concurrent requests, you can never let the thread wait for blocking, synchronous, or long-running operations. A distinctive feature of Node.js is that it is designed and implemented from top to bottom to achieve asynchronous. This makes it very suitable for event-type programs.
Unfortunately, there is still a possibility that synchronous/blocking calls will occur. For example, many file system operations have both synchronous and asynchronous versions, such as writeFile and writeFileSync. Even if you use code to control the synchronization method, it is still possible to use the external function library that blocks calls inadvertently. When you do this, the impact on performance is huge.
// Good: write files asynchronouslyfs.writeFile('message.txt', 'Hello Node', function (err) { console.log("It's saved and the server remains responsive!");}); // BAD: write files synchronouslyfs.writeFileSync('message.txt', 'Hello Node');console.log("It's saved, but you just blocked ALL requests!");Our initialization log unintentionally includes a synchronous call to write content to disk when implemented. If we don't do performance testing, it will be easy to ignore this problem. When using a node.js instance in the developer box as a standard test, this synchronous call will cause performance to drop from thousands of requests per second to only a few dozen.
2. Close the socket pool
Node.js' http client will automatically use socket pools: By default, it will limit only 5 sockets per host. Although reuse of sockets may cause the increase in resources under control, if you need to deal with concurrent requests from the same host, it will lead to a series of bottlenecks. In this case, it is a good idea to increase the value of maxSockets or close the socket pool:
// Disable socket pooling var http = require('http');var options = {.....};options.agent = false;var req = http.request(options)3. Don't let static resources use Node.js
For static resources such as css and images, use standard WebServer instead of Node.js. For example, LinkedIn Mobile uses nginx. We also use content delivery networks (CDNs), which can copy static resources around the world to servers. This has two benefits: (1) It can reduce the load on our node.js server (2) CDNs can allow static content to be delivered on servers closer to the user, thereby reducing the waiting time.
4. Render on the client
Let's quickly compare the difference between server rendering and client rendering. If we use node.js to render on the server side, for each request we will send back an HTML page like the following:
<!-- An example of a simple webpage rendered entirely server side --> <!DOCTYPE html><html> <head> <title>LinkedIn Mobile</title> </head> <body> <div> <img src="http://mobile-cdn.linkedin.com/images/linkedin.png"/> </div> <div> Hello John! </div> </body></html>
Please pay attention to observing all the contents of this page, except for the user's name, the rest is static: the contents overloaded by each user and the page are the same. Therefore, a more effective approach is to let Node.js return only the dynamic content required by the page in JSON form.
{"name": "John"}
The rest of the page - all static HTML tags - can be placed in JavaScript templates (such as the underscore.js template):
<!-- An example of a JavaScript template that can be rendered client side --> <!DOCTYPE html><html> <head> <title>LinkedIn Mobile</title> </head> <body> <div> <img src="http://mobile-cdn.linkedin.com/images/linkedin.png"/> </div> <div> Hello <%= name %>! </div> </body></html>
The performance improvement comes from these places: As the third point says, static JavaScript templates can be provided on the server side through webserver (such as nginx), or implemented with better CDNs. In addition, JavaScript templates can be cached in the browser or stored locally. After all the initial pages are loaded, the only data that needs to be sent to the client is JSON, which will be the most effective. This method can greatly reduce the load of CPU, IO, and Node.js.
5. Use gzip
Many servers and clients support gzip to compress requests and answers. Whether you are answering a client or sending a request to a remote server, make sure to make full use of it.
6. Parallelization
Try to let all your blocking operations - send requests, DB calls, and file system access parallelization to remote services. This will reduce the waiting time for the slowest blocking operation, rather than the waiting time for all blocking operations. To keep callbacks and error handling clean, we use Step to control the traffic.
7.Session liberalization
LinkedIn Mobile uses the Express framework to manage request/reply cycles. Many express examples include the following configuration:
app.use(express.session({ secret: "keyboard cat" }));
By default, session data is stored in memory, which adds huge overhead to the server, especially as the number of users increases. You can use an external session store, such as MongoDB or Redis, but each request will result in the overhead of remote calls to get session data. When possible, the best option is to store all stateless data on the server side. By liberalizing the session by not including the above express configuration, you will see better performance.
8. Use binary modules
If possible, replace JavaScript modules with binary modules. For example, when we convert from a SHA module written in JavaScript to a compiled version of Node.js, we see a big leap forward in performance:
// Use built in or binary modulesvar crypto = require('crypto');var hash = crypto.createHmac("sha1",key).update(signatureBase).digest("base64");9. Replace client library with standard V8 JavaScript
Many JavaScript libraries are created for use on web browsers because in JavaScript environments, for example, some browsers support functions such as forEach, map, and reduce, but some browsers do not. Therefore, client libraries usually use a lot of inefficient code to overcome browser differences. On the other hand, in Node.js, you can know exactly which JavaScript methods are effective: V8 JavaScript engine supports Node.js to implement the ECMAScript specified in the fifth edition of ECMA-262. Directly replace the client library with standard V8 JavaScript functions, you will find significant performance improvements.
10. Keep your code small and light
Using a mobile device will make access slow and latency high, which tells us to keep our code small and light. The same philosophy is maintained for server code. Occasionally look back at your decision and ask yourself questions like: "Do we really need this module?", "Why do we use this framework? Is its overhead worth our use?", "Can we implement it in a simple way?". Small and light code is usually more efficient and fast.
Try it
We work hard to make our mobile apps fast. Try it on platforms such as IPhone apps, Android apps, and HTML5 mobile versions to let us know how we are doing.