Understanding the I / O model of your application can lead to an understanding of the differences between the application that works with the load under which it was created, and that face to face with the real way of its application. Perhaps, if your application is small and does not create a large load, then it is not so important for it. But as traffic grows, using an erroneous I / O model can plunge you into a world of pain.
As in most other situations with several possible solutions, the matter is not which of the options is better, the point is in the understanding of compromises. In this article, we will compare Node, Java, Go and PHP from under Apache, discuss input-output models in different languages, consider the advantages and disadvantages of each model, and run some simple benchmarks. If you are concerned about the I / O performance of your next web application, then this article is for you.
To understand the factors related to I / O, you first need to recall some of the concepts used at the OS level. It is unlikely that you will have to deal with many of them directly, most likely, you will work with them indirectly, through the application's runtime environment. And the details play an important role.
Take first the system calls that can be described as:
Your program (in the so-called user space) must ask the operating system kernel to perform an I / O operation on behalf of your program.
System calls are the way in which the program asks the kernel to do something. The specificity of their implementation depends on the OS, but the basic principle is the same everywhere. There must be some specific instruction for transferring control from your program through the kernel (as a function call, only with a special “additive” to work in such a situation). In general, system calls are blocking, that is, the program waits until the kernel returns to your code.
It was said above that system calls are blocking, and in general this is the case. However, some calls can be described as non-blocking. This means that the kernel accepts your request, puts it in a queue or some kind of buffer, and then, without any waiting, immediately returns to the currently executing I / O. So "blocking" occurs only for a very short period of time, sufficient for placing your request in the queue.
To make it clearer, here are some examples (of Linux system calls):
read()
— : (handle), , ; , . .epoll_create()
, epoll_ctl()
epoll_wait()
— , , , ; / ; , . / . , , .. 3 , - , 3 (3 ). , . , , : , 200 (1/5 ). 20 , — 200 . 10 .
(« »), (« , ») /. .
, , .
. , , . . , , . 300 , , : , . , / .
— - . , 100 , 1000 , , / , . .
( ), . , , .
: « , ». / .
? : , , … .
: ( , ). , (Memcache . .) , , , /, , . , / (PHP, Java), HTTP- : /, , .
. . , /, / , .
1990- Converse CGI- Perl. PHP, , -.
PHP . , PHP- .
HTTP- - Apache. , , ( , , ). Apache PHP .php
-, . PHP- /. PHP file_get_contents()
, read()
.
, , :
<?php
// blocking file I/O
$file_data = file_get_contents(‘/path/to/file.dat’);
// blocking network I/O
$curl = curl_init('http://example.com/example-microservice');
$result = curl_exec($curl);
// some more blocking network I/O
$result = $db->query('SELECT id, data FROM examples ORDER BY id DESC limit 100');
?>
:
: . / . ? , . ? 20 . , / (epoll .). , , , .
: Ruby , , .
Java , , « ». Java (multithreading) — ( ).
Java - , , , .
/ Java Servlet :
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
// blocking file I/O
InputStream fileIs = new FileInputStream("/path/to/file");
// blocking network I/O
URLConnection urlConnection = (new URL("http://example.com/example-microservice")).openConnection();
InputStream netIs = urlConnection.getInputStream();
// some more blocking network I/O
out.println("...");
}
doGet
, , , . , , . , PHP. , / , . (pooled), , , , .
Java 1.4 ( 1.7) /. , - , , . Java - - , Java- , .
Java /, , , / - .
Node.js /. , Node, , , /. . , , .
, Node, : : « », : « ». , /, callback-, Node .
Node / :
http.createServer(function(request, response) {
fs.readFile('/path/to/file', 'utf8', function(err, data) {
response.end(data);
});
});
callback-. , . — .
, Node / callback-. : Node. , : Node callback-; /, , callback-. / callback- (event loop). .
. JS- V8 (JS- Chrome, Node). JS-, , . . , / , JS , . , : , , . :
var handler = function(request, response) {
connection.query('SELECT ...', function (err, rows) {
if (err) { throw err };
for (var i = 0; i < rows.length; i++) {
// do processing on each row
}
response.end(...); // write out the results
})
};
Node / , , , for
, . 10 , , , . .
, / — , , , . - , .
— , — , - , . Node- , .
. Node , /. , , HTTP-, , , .
Go, , . , , .
- , Go /. — . Go . , , — . , HTTP- Go, .
:
runtime- Go, /, // . ., , , .
runtime- Go , , Node. , / . Go , - , : Go , . :
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
// the underlying network call here is non-blocking
rows, err := db.Query("SELECT ...")
for _, row := range rows {
// do something with the rows,
// each request in its own goroutine
}
w.Write(...) // write the response, also non-blocking
}
, , , , /.
« ». /; , . Go . , , /. , , .
Go , /.
. . HTTP- . , «HTTP-/» , .
, 64- , , N SHA-256 (N URL-, , .../test.php?n=100
) . / .
(low concurrency). 2000 300 (N = 1):
. ,
- . , , , /. , ( , ).
N 1000, 300 — , ( ):
. ,
Node, , , . , PHP ( ) Java. , SHA-256 PHP , (execution path) , 1000 .
5000 (N = 1) . , . .
. ,
. , , PHP + Apache , , PHP. Go , Java, Node, — PHP.
, , , , .
, , , /.
, , PHP Java /, -. , , . , , . «» PHP Java - .
, , , :
vs. | I/O | ||
---|---|---|---|
PHP | |||
Java | |||
Node.js | |||
Go | () |
. , /, / . , Go.
, , , . - Node Go. / — , . 15 .
, , , , . !
Source: https://habr.com/ru/post/329258/
All Articles