node. js

Source: Internet
Author: User

JavaScript single thread misunderstanding

When I touch JavaScript (no matter the browser or the Nodejs), I always encounter the need for a friend with multiple threads. And in the Nodejs aspect, has the friend even directly said, Nodejs is single-threaded, cannot very good use multicore CPU.

Admittedly, in front-end browsers, because the front-end JavaScript and UI occupy the same thread, executing JavaScript does pose a certain level of annoyance to the UI response. However, JavaScript does not pose a significant problem for front-end applications unless you are using a very large loop statement to execute JavaScript, or with blocking Ajax, or too frequent timer execution. So few friends complain that JavaScript is single-threaded and does not make good use of multicore CPUs because there is no performance bottleneck.

However, we can use Ajax and web worker to respond to this misunderstanding. When the AJAX request is sent, the rest of the JavaScript code is executed quickly, unless it is a synchronous request. It is necessary that the network request does not block the execution of JavaScript until the time the Ajax is sent and the response is received. Well, the answer is obvious, JavaScript is actually executed on a single thread, but the host (browser) of the entire web app execution is not executed in a single thread. The birth of Web worker is a direct way to solve the UI response problem caused by JavaScript and UI, it can open a new thread to execute JavaScript.

In the same way, JavaScript in Nodejs is actually executed on a single thread, but as the host's Nodejs itself is not single-threaded, Nodejs in the I/O aspect is also used to assist in the asynchronous implementation of a small number of additional threads. Programmers don't have the opportunity to create threads directly, which is why some students take it for granted that Nodejs's single thread doesn't make good use of multicore CPUs, and they might even say that it's hard to imagine a single-threaded program being developed collaboratively by multiple people.

Nodejs encapsulates an internal asynchronous implementation, which causes the programmer to be unable to manipulate the thread directly, causing all the business logic operations to be dropped on the JavaScript thread, which means that the I/O problem is well resolved at high concurrent requests. But all of the business logic operations are running on JavaScript threads all of the way, creating a crowded JavaScript thread. Nodejs's weaknesses at this time will be exposed, single-threaded operation to form the bottleneck, slowing down the efficiency of I/O. This can be considered a disadvantage of not being able to take advantage of multicore CPUs in dense computing situations. This congested JavaScript thread gives the I/O a performance cap.

But things are not absolute. Back to the front-end browser, in order to solve the thread congestion situation, the Web worker emerged. In the same way, node also provides child_process.fork to create a child process for node. In the case where a node process is able to solve the dense I/O, the rest of the node can be used as a resident service to solve the problem of operation blocking (distributing the operation to multiple node processes, similar to Apache creating multiple child processes). Of course, child_process/web worker mechanism can only solve the problem of single machine, big Web application is impossible to complete all the request service with one server. The advantage of Nodejs on I/O is that it is not a problem to communicate across multiple OS node. The answer to solving Nodejs's computationally intensive problems is also very simple, which is to distribute the operations to multiple CPUs. Please refer to the Multi-node performance test after the article, you can see in the Multi-node process scenario, the speed of responding to requests has been greatly improved.

In the writing of the article, node's latest release of 0.5.10 added the cluster startup parameters. The parameters are used in the following ways:

Node Cluster server.js

When node is started, when this parameter is attached, node detects the number of CPUs on the machine to determine how many process instances are started, and these instances automatically share the same listening port. Please refer to the official documentation for details. In the previous solution-intensive problem, the engineer needed to multi-node such a library or other scheme to manually distribute the request, and with the support of the cluster parameter, it was possible to release much of the effort of the engineer to solve the problem.

Event-Type programming

Continuation of the previous section of the discussion. We know that Nodejs/javascript has an asynchronous nature, and as can be seen from the Nodejs API design, any operations involving I/O are almost always designed as event callbacks, and most classes inherit from Eventemitter. The benefits of this are two, one is to take full advantage of the non-blocking I/O features to improve performance, and another benefit is to encapsulate the underlying thread details, leaving the business focus on the event message to the programmer, without having to focus on the many technical details involved in multithreaded programming.

From a realistic point of view, event-based programming is also more realistic. Take a business scenario for example: Housewives prepare Chinese food at home, she needs to complete two dishes, a mix of cucumber, a tomato egg soup. In PHP, for example, housewives will first finish mixing cucumber, and then complete the tomato egg soup, is executed sequentially/serially. But now suddenly a little accident, the salad cucumber needs to run out of soy sauce, need her son go out to help her buy soy sauce back. Then the PHP housewife is calling her son to go out soy sauce This period of time is a waiting state, until the soy sauce to buy back, will continue to make the next dish. Then, in the nodejs of the housewife will be what a scene, it is clear that in the time waiting for her son soy sauce back, she can suspend the production of cold cucumber, and the process of direct tomato egg soup, the son after the soy sauce back, continue to complete her salad cucumber. There is no time to waste waiting. The instance pseudo-code is as follows:

var mother = new People ();
var child = new People ();
Child.buysoy (function (soy) {
Mother.cook ("cucumber", soy);
});
Mother.cook ("tomato");

Next, convert the above code to code based on the Event/task Asynchronous Pattern:

var proxy = new Eventproxy ();
var mother = new People ();
Proxy.bind ("Cook_cucumber", function (soy) {
Mother.cook ("cucumber", soy);
});
Proxy.bind ("Cook_tomato", function () {
Mother.cook ("tomato");
});
var child = new People ();
Child.buysoy (function (soy) {
Proxy.trigger ("cucumber", soy);
});
Proxy.trigger ("tomato");

The code is a lot more, but the business logic point is clear: Cook_cucumber and Cook_tomato two tasks are predefined by the Bind method. The Bind method here can be thought of as an await message implementation, requiring the first parameter to identify the name of the task, and the message generated by the process during execution triggers the execution of these tasks. As you can see, in event programming, the user only needs to focus on the few business events that it requires, and the waiting in the middle is provisioned from the bottom up. The code here is just an example of an Event/task asynchronous Pattern, and in a simple scenario, the first piece of code can. Do Nodejs programming, will feel more in the reality of the business scene design and task scheduling, there is no order to ensure that the program structure is more like a state machine.

Personally, in event programming, programmers need to change their minds to accept and play this asynchronous/non-blocking advantage. Similarly, one of the problems with this type of event programming is that the business logic is loose and fragmented. This is a painful thing for a classmate accustomed to sequential, promise programming, and the spread of business logic is a big problem for people who are clearly designed at the outset.

I mentioned that event programming is closer to real life and more natural, so this style of programming also leads to your code being as complex as your life. Fortunately, your own life to face, for a project, do not need everyone to design the entire big business logic, for architects, business logic is clear, with the benefit of the business logic loosely coupled with event programming, after the large framework is set, the business logic is divided into the appropriate granularity, For every programmer who implements a business point, there is no such pain. The 28 principle is very effective in this place.

Deep Nested callback issues

Javascript/nodejs is easy to handle for a single asynchronous event, but where the problem arises is "results collaboration between multiple asynchronous events." Take the NODEJS server rendering page For example, rendering needs data, templates, localized resource files, these three parts are to be obtained asynchronously, the native code will result in nesting, because only in this way can guarantee the time of rendering data, templates, localization resources have been obtained. The problem is that the three steps are virtually decoupled, but because native code does not have a promise mechanism, the steps that can be executed in parallel (taking full advantage of nonblocking I/O) become a serial execution process that directly degrades performance. The code is as follows:

var render = function (template, data) {
_.template (template, data);
};



Render (template, data);
});
});
});

Faced with this code, many engineers have expressed their discomfort. This weakness has also formed a moderate obstacle to the promotion of Nodejs. For the pursuit of performance and maintenance of students, certainly not satisfied with the above practices. I have a slight preference for JavaScript events and callbacks, and think that events, callbacks, parallelism, and loose coupling can be agreed upon. The following section of code is implemented with Eventproxy:

var proxy = new Eventproxy ();
var render = function (template, data, l10n) {
_.template (template, data);
};
Proxy.assign ("template", "Data", "l10n", Render);

Proxy.trigger ("template", template);
});

Proxy.trigger ("Data", data);
});

Proxy.trigger ("l10n", l10n);
});

The code volume looks slightly more than the native implementation, but is logically clear. Template, data, localization resources parallel acquisition, performance improvement is self-evident, the Assign method takes full advantage of the event mechanism to ensure the correctness of the final result. In events, callbacks, parallel, loosely coupled, the desired requirements are met on several points.

For more eventproxy details, refer to the official page.

Extension of the depth callback problem

Eventproxy the way to solve the depth callback is entirely based on the event mechanism, which needs to be established in the identification of event programming, then there must be the students who do not agree with the event-based programming, and the habit of sequential, promise-style, to its promotion Bind/trigger mode is very difficult for them to accept. Jscex and Streamline.js are currently the most mature synchronous programming solutions. Can be programmed by synchronous thinking, the final code is through the compiled object code, through the tool to assist the user to change the thinking.

Conclusion

For good things, we can not abandon them because of their superficial flaws, there will always be a compromise solution to meet the demand. The effectiveness of Nodejs in real-time is evident, even if there are some obvious shortcomings, but as some solutions emerge, I believe there is nothing to block the pace of progress.

Appendix (Concurrency Testing in multi-core environments)

Server environment:

    • Network Environment: Intranet
    • Stress test Server:
    • Server System: Linux 2.6.18
    • Server configuration: Intel (R) Xeon (TM) CPU 3.40GHz 4 CPUS
    • Memory: 6GB
    • Nodejs version: v0.4.12

Client test environment:

    • Contract Tool: Apache 2.2.19 's own AB test tool
    • Server System: Linux 2.6.18
    • Server configuration: Pentium (R) dual-core CPU E5800 @ 3.20GHz 2CPUS
    • Memory: 1GB

Single Thread node code:

var http = require (' http ');
var server = Http.createserver (function (request, response) {
var j = 0;
for (var i = 0; I & lt; 100000; i++) {
j + = 2/3;
}
Response.End (j + ");
});
Server.listen (8881);
Console.log (' Server running at http://10.1.10.150:8881/');

Four-process Node code:

var http = require (' http ');
var server = Http.createserver (function (request, response) {
var j = 0;
for (var i = 0; I & lt; 100000; i++) {
j + = 2/3;
}
Response.End (j + ");
});
var nodes = require ("./lib/multi-node"). Listen ({
port:8883,
Nodes:4
}, server);
Console.log (' Server running at http://10.1.10.150:8883/');

Here is a brief introduction to multi-node this plugin, this plug-in is the use of require ("child_process"). Spawn () method to create multiple child threads. Because both floating-point and string concatenation are CPU-intensive operations, here we loop 10W times, each to J plus 0.66666. At the end of the comparison, the multi-child process node is much faster than the single-process node in CPU-intensive computing.

Here are the test results:

Comm.

500/30

500/30

1000/30

1000/30

3000/30

300 0/30

Ty PE

single process

multiple child processes

Single-process

multiple child process

single process

multiple child processes

RPS

2595

5597

2540

5509

2571

5560

TPQ

0.38

0.18

0.39

0.19

0.39

0.18

80% REQ

72

65

102

85

157

142

Fail

0

0

0

0

0

0

Description

    • Single process: Only one node. js process is open.
    • Multi-Child process: Open a node. JS process, and open 3 of its child processes.
    • 3000/30: Delegate command./ab-c 3000-t-http://10.1.10.150:8888/. 3,000 clients, up to 30 seconds, up to 5W requests.
    • RPS: Represents the number of requests processed per second, and the main indicator of concurrency.
    • TPQ: The time per request processing, in milliseconds.
    • Fail: Represents the average number of failed requests processed.
    • 80% Req: The number of milliseconds that the request representing 80% is returned.

From the results and graphs: Many sub-processes can significantly alleviate the CPU utilization of node. js and increase the CPU-intensive computing power of node. js.

Figure 1: node. js for a single process under stress testing, unable to take full advantage of the server performance of the 4-core CPU.

Figure 2: Multi-process node, can take advantage of the 4-core CPU to squeeze dry server performance.

Figure 3: A multi-child process, you can see that altogether ran 4 processes.

node. js

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.