16. Уроки Node.js. Событийный цикл, библиотека libUV. Часть 1.
Всем привет. Если вы привыкли глубоко вникать в происходящее, то эта статья для вас. Здесь мы разберем те вопросы, которые рано или поздно обязательно возникнут при разработке, и ответа на которые требует глубокого понимания, как именно работает Node.js. Например, здесь (serverAsync.js смотрите файлы нашего предыдущего урока) для чтения файла использован асинхронный вызов:
1 2 3 4 5 6 7 8 9 10 | fs.readFile('index.html', function(err, info) { if(err) { console.error(err); res.statusCode = 500; res.end("A server error occurred "); return } res.end("info"); }); |
Но любые ли операции можно сделать асинхронными? Насколько действительно опасны асинхронные вызовы и что делать, если асинхронная операция есть, и избежать ее никак нельзя? Как снизить ее вредный эффект? Что происходит с теми запросами, которые приходят, пока интерпретатор занят? Например, если здесь (serverSync.js) есть синхронные файлы:
1 | info = fs.readFileSync('index.html'); |
и пришло 10 клиентов. Получают ли они отказ в обслуживании? Или выстраивается очередь из них? Ответы на эти и многие другие вопросы Вы получите во время чтения этой статьи.
Для того, чтобы глубже понимать происходящее, познакомимся с библиотекой libUV. К этой библиотеке не надо обращаться каким-то явным образом. Она написана на языке С и встроена в сервер Node.js. Библиотека libUV отвечает за две принципиально важные вещи. Первое – это кроссплатформенные операции ввода-вывода, работа с файлами, работа с сетью. Мы через JavaScript даем команду Node.js просчитать какой-то файл или отправить данные по сети. А Node.js чтобы это сделать уже внутри себя использует библиотеку libUV. Таким образом, libUV отвечает за кроссплатформенную реализацию этих функций. Именно она уже знает, как работать с Windows, Linux и т.д.
Вторая область ответственности libUV это поддержка основного событийного цикла Node.js. Оказывается, когда мы запускаем какой-то скрипт, то он запускается в режиме цикла. Этот цикл чередует выполнение JavaScript, который обеспечивается виртуальной машиной V8 с ожиданием различных событий: ввода-вывода, срабатыванием таймеров, за которые также отвечает библиотека libUV. Этот цикл будет продолжаться до тех пор, пока возможно появление каких-то новых событий, ввода-вывода или таймера, которые нужно будет обработать.
Для примера разберем, что происходит при запуске вот такого сервера:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | var http = require('http'); var fs = require('fs'); var server = new http.Server(); server.on('request', function(req, info) { if (req.url == '/') { fs.readFile('index.html', function(err, info) { if(err) { console.error(err); res.statusCode = 500; res.end("A server error occurred "); return } res.end("info"); }); } else { /*404 */ } }); server.listen(3000); |
Вначале срабатывает JavaScript. Он подключает модули:
1 2 | var http = require('http'); var fs = require('fs'); |
Создает объект:
1 | var server = new http.Server(); |
Вешает обработчик:
1 | server.on('request', function(err, info) { |
То, что внутри этой функции, пока неважно, обработчик пока еще не сработал. Последняя строчка – это вызов команды listen.
1 | server.listen(3000); |
Это команда работы с сетевыми соединениями.
Она, попадая в Node.js, проходит через его С++ код, превращается в вызов внутреннего метода TCPWrap::listen. Этот внутренний метод уже вызывает библиотеку libUV, а именно, ее метод uv__listen, который как раз осуществляет всю работу. Он, в зависимости от операционной системы, вешает обработчик соединений на данный порт. К примеру для unix-системы используется системный вызов listen. Таким образом, libUV назначила обработчик, на соединение, на этот порт.
Этот обработчик, или в терминах libUV он называется watcher, является внутренним, то есть, мы к нему доступа не имеем. Это именно libUV его поставила, и когда что-то произойдет, например, когда придет новое соединение, то watcher сработает, вызовет соответствующие методы libUV, Node.js и, скорее всего, в конечном счете, даст нам какое-нибудь событие, например, connection. Но это все будет потом. А пока что listen просто повесила обработчик. Результат этого действия поднимается вверх по цепочке. Если все хорошо, то это приводит к событию listening в JavaScript. Если обработчик повесить не удалось, то error.
Материалы взяты из данного скринкаста
We are looking forward to meeting you on our website soshace.com
No comments yet