Разработка Web-приложения с использованием JavaScript каркаса Node.js

 

Министерство образования и науки РФ

Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования

"Рязанский государственный радиотехнический университет"








Курсовая работа

по дисциплине: «Интернет-технологии»

на тему: «РазработкаWeb-приложения с использованием JavaScript каркаса Node.js»




Выполнил: студент 1 курса,

группы 345М

Симаков А.Ю.

Проверил: доцент

Бакулев А.В.






Рязань 2013


Содержание


Введение

.Что такое NODE?

.1Что позволяет делать Node?

.2Почему имеет смысл использовать Node?

.3Архитектура: потоки или асинхронный ввод/вывод с управлением по событиям

.4Производительность и использование процессора

.5Использование серверов, экономия затрат и экологичный Интернет

.Характеристики NODE

.1Системные требования

.2Запуск Node-серверов на этапе инициализации системы

.3Использование всех процессорных ядер в многоядерной системе

.Модули Node

.1Как Node ищет модули, затребованные в require('module')?

.2Менеджер пакетов для Node (npm)

.Хранение и выборка данных

.1Движки сохранения данных для Node

.2SQLite3 - облегченная встраиваемая база данных на основе SQL

.3Mongoose - интерфейс между Node и MongoDB

.Практический пример на основе продолжительных вычислений (числа Фибоначчи)

Заключение

Список использованной литературы

node сервер приложение данный


Введение

(другое название - Node,js)- это недавно появившаяся платформа, которая выводит язык JavaScript за пределы браузера и позволяет использовать его в серверных приложениях. В основе платформы лежит исключительно быстрый движок JavaScript, заимствованный из браузера Chrome, V8, к которому добавлена быстрая и надежная библиотека асинхронного сетевого ввода/вывода. Основной упор в Node делается на создании высокопроизводительных, хорошо масштабируемых клиентских и серверных приложений для «веб реального времени».

Эту платформу разработал Райан Дал (RyanDahl) в 2009 году, после двух лет экспериментирования с созданием серверных веб-компонентов на Ruby и других языках. В ходе своих исследований он пришел к выводу, что вместо традиционной модели параллелизма на основе потоков следует обратиться к событийно-ориентированным системам. Эта модель была выбрана за простоту (хорошо известно, что многопоточные системы трудно реализовать правильно), за низкие накладные расходы, по сравнению с идеологией «один поток на каждое соединение», и за быстродействие. Цель Node - предложить «простой способ построения масштабируемых сетевых серверов». При проектировании за образец были взяты такие системы, как EventMachine (Ruby) и каркас Twisted (Python).



. Что такое NODE?

- это захватывающая новая платформа для разработки веб-приложений, серверов приложений, произвольных сетевых серверов и клиентов, да и вообще для программирования. Она спроектирована так, чтобы обеспечить высочайшую масштабируемость сетевых приложений - за счет хитроумного сочетания асинхронного ввода/вывода, использования JavaScript на стороне сервера, изобретательного использования анонимных функций JavaScript и однопоточной событийно-ориентированной архитектуры.

Принятая в Node модель принципиально отличается от распространенных платформ для построения серверов приложений, в которых масштабируемость достигается за счет многопоточности. Утверждается, что благодаря событийно-ориентированной архитектуре снижается потребление памяти, повышается пропускная способность и упрощается модель программирования. Сейчас платформа Node быстро развивается, и многие считают ее привлекательной альтернативой традиционному подходу к разработке веб-приложений - на базе Apache, РНР, Python и т. п.

В основе Node лежит автономная виртуальная машина JavaScript с расширениями, делающими ее пригодной для программирования общего назначения с упором на разработку серверов приложений. Платформу Node не имеет смысла напрямую сравнивать ни с языками программирования, которые обычно используются для создания веб-приложений (PHP/Python/Ruby/Java и прочие), ни с контейнерами, реализующими протокол HTTP (Apache/Tomcat/Glassfish ит. д.). В то же время многие считают, что потенциально она может заменить традиционные стеки веб-приложений.

В основе реализации лежит цикл обработки событий неблокирующего ввода/вывода и библиотеки файлового и сетевого ввода/вывода, причем все это построено поверх движка V8 JavaScript (заимствованного из веб-браузера Chrome). Библиотека ввода/вывода обладает достаточной общностью для реализации любого протокола на базе TCP или UDP: DNS, HTTP, IRC, FTP и др. Но хотя она поддерживает разработку серверов и клиентов произвольного протокола, чаще всего применяется для создания обычных веб-сайтов, где заменяет Apache/PHP или Rails.


.1 Что позволяет делать Node?

- платформа для написания JavaScript-приложений вне веб-браузера. Это не тот JavaScript, с которым все мы знакомы по опыту работы с браузерами. В Node не встроена ни объектная модель документа (DOM), ни какие-либо ещё возможности браузера. Именно язык JavaScript в сочетании с асинхронным вводом/выводом делает Node мощной платформой для разработки приложений. Но вот для чего Node непригодна, так это для разработки персональных приложений с графическим интерфейсом пользователя (ГИП). На сегодняшний день в Node нет встроенного эквивалента Swing (или SWT). Нет и подключаемой библиотеки ГИП для Node, и внедрить Node в браузер тоже нельзя. Если бы для Node существовала библиотека ГИП, то на этой платформе можно было строить и персональные приложения. Недавно появилось несколько проектов по созданию интерфейса между Node и GTK, итогом которых должна стать кросс-платформенная библиотека ГИП. В состав движка V8, используемого в Node, входят API-расширения, позволяющие писать на C/C++ код для расширения JavaScript или интеграции движка с платформенными библиотеками.

Помимо встроенного умения исполнять код на JavaScript, включенные в состав дистрибутива модули предоставляют и другие возможности:

утилиты командной строки (для включения в скрипты оболочки);

средства написания интерактивных консольных программ (цикл «чтение - выполнение - печать»);

великолепные функции управления процессами для наблюдения за дочерними процессами;

объект Buffer для работы с двоичными данными;

механизм для работы с сокетами TCP и UDP с полным комплектом обратных вызовов в ответ на события;

поиск в системе DNS;

средства для создания серверов и клиентов протоколов HTTP и HTTPS, построенные на основе библиотеки ТСР-сокетов;

средства доступа к файловой системе;

встроенная рудиментарная поддержка автономного тестирования с помощью утверждений.

Сетевой слой Node находится на низком уровне, но работать с ним все равно просто. Например, модули HTTP позволяют реализовать HTTP-сервер (или клиент), написав всего несколько строк кода, но, тем не менее, на этом уровне программист работает очень близко к реальным запросам по протоколу и может точно указать, какие HTTP-заголовки следует включать в ответ на запрос. Если программист на РНР обычно не интересуется заголовками, то для программиста на Node они существенны.

Иными словами, написать на Node HTTP-сервер очень просто, но типичному разработчику веб-приложений нет нужды работать на таком низком уровне. Например, кодируя на РНР, программист предполагает, что Apache уже присутствует, так что реализовывать серверную часть стека ему не нужно. Сообщество, сложившееся вокруг Node, создало широкий спектр каркасов для разработки веб-приложений, в том числе Connect, которые позволяют быстро сконфигурировать HTTP так, чтобы предоставлялось все, к чему мы привыкли, - сеансы, куки, обслуживание статических файлов, протоколирование и т.д. - и пусть разработчик занимается бизнес-логикой приложения.

.2 Почему имеет смысл использовать Node?


Язык JavaScript очень популярен благодаря присутствию в любом веб-браузере. Он ни в чем не уступает другим языкам, но при этом поддерживает многие современные представления о том, каким должен быть язык программирования. Благодаря широкому распространению имеется немало опытных программистов на JavaScript.

Это динамический язык со слабо типизированными, динамически расширяемыми объектами, которые неформально объявляются по мере необходимости. Функции в нем являются полноценными объектами и обычно используются в виде анонимных замыканий. Это делает JavaScript более мощным языком, по сравнению с некоторыми другими, часто применяемыми для разработки веб-приложений. Теоретически наличие подобных возможностей должно повышать продуктивность программистов. Но споры между сторонниками динамических и статических языков, а также строгой и слабой типизации до сих пор не утихли и вряд ли когда-нибудь утихнут.

Один из основных недостатков JavaScript - Глобальный Объект. Все переменные верхнего уровня «сваливаются» в Глобальный Объект, и при использовании одновременно нескольких модулей это может привести к неуправляемому хаосу. Поскольку веб-приложения обычно состоят из множества объектов, возможно, создававшихся разными организациями, то может возникнуть опасение, будто программирование для Node сродни хождению по минному полю, нашпигованному конфликтующими между собой глобальными объектами. Однако это не так. На самом деле в Node используется система организации модулей CommonJS, а это означает, что локальные переменные некоторого модуля так и будут локальными в нем, пусть даже выглядят как глобальные. Такое четкое разграничение между модулями решает проблему Глобального Объекта.

Использование единого языка программирования на сервере и на клиенте давно уже было мечтой разработчиков для веб. Своими корнями эта мечта уходит в период становления Java, когда апплеты представлялись клиентским интерфейсом к написанным на Java серверным приложениям, a JavaScript первоначально виделся как облегченный скриптовый язык взаимодействия с апплетами. Но что-то на этом пути не сложилось, и в результате не Java, a JavaScript стал основным языком на стороне клиента-браузера. С появлением Node мы, наконец, сможем реализовать мечту - сделать JavaScript языком, используемым по обе стороны веб - на стороне клиента и сервера.

У единого языка есть несколько потенциальных плюсов:

одни и те же программисты могут работать над обеими сторонами приложения;

код проще переносить с сервера на клиент и обратно;

общий для клиента и сервера формат данных (JSON);

общий программный инструментарий;

общие для клиента и сервера средства тестирования и контроля качества;

на обеих сторонах веб-приложения можно использовать общие шаблоны представлений;

общий язык общения между группами, работающими над клиентской и серверной частью.упрощает реализацию этих (и других) достоинств, предлагая основательную платформу и активное сообщество разработчиков.

Архитектура: потоки или асинхронный ввод/вывод с управлением по событиям.

Говорят, что именно благодаря асинхронной событийно-ориентированной архитектуре Node демонстрирует столь высокую производительность. Так и есть, только к этому надо добавить еще стремительность движка V8 JavaScript. В традиционной модели сервера приложений параллелизм обеспечивается за счет использования блокирующего ввода/вывода и нескольких потоков. Каждый поток должен дожидаться завершения ввода/вывода, перед тем как приступить к обработке следующего запроса.

В Node имеется единственный поток выполнения, без какого-либо контекстного переключения или ожидания ввода/вывода. При любом запросе ввода/вывода задаются функции обработки, которые впоследствии вызываются из цикла обработки событий, когда станут доступны данные или произойдет еще что-то значимое. Модель цикла обработки событий и обработчика событий - вещь распространённая, именно так исполняются написанные на JavaScript скрипты в браузере. Ожидается, что программа быстро вернет управление циклу обработки, чтобы можно было вызвать следующее стоящее в очереди задание. Чтобы повернуть наши мысли в нужном направлении, Райан Дал (в презентации «Cincode Node») спрашивает, что происходит при выполнении такого кода:


result = query('SELECT * from db);


Разумеется, программа в этой точке приостанавливается на время, пока слой доступа к базе данных отправляет запрос базе, которая вычисляет результат и возвращает данные. В зависимости от сложности запроса его выполнение может занять весьма заметное время. Это плохо, потому что пока поток простаивает, может прийти другой запрос, а если заняты все потоки, то запрос будет просто отброшен. Расточительно это как-то. Да и контекстное переключение обходится не бесплатно; чем больше запущено потоков, тем больше времени процессор тратит на сохранение и восстановление их состояния. Кроме того, стек каждого потока занимает место в памяти. И просто за счет асинхронного событийно-ориентированного ввода/вывода Node устраняет большую часть этих накладных расходов, привнося совсем немного собственных.

Часто рассказ о реализации параллелизма с помощью потоков сопровождается предостережениями типа «дорого и чревато ошибками», «ненадежные примитивы синхронизации в Java» или «проектирование параллельных программ может оказаться сложным, и не исключены ошибки» (фразы взяты из результатов, выданных поисковой системой). Причиной этой сложности являются доступ к разделяемым переменным и различные стратегии предотвращения взаимоблокировок и состязаний между потоками. «Примитивы синхронизации в Java» - один из примеров такой стратегии, и, очевидно, многие программисты считают, что пользоваться ими трудно. Чтобы как-то скрыть сложность, присущую многопоточному параллелизму, создаются каркасы типа java.util.concurrent, но все равно некоторые считают, что попытка упрятать сложность подальше не делает проблему проще.призывает подходить к параллелизму по-другому. Обратные вызовы из цикла обработки событий - гораздо более простая модель параллелизма, как для понимания, так и для реализации.

Чтобы пояснить необходимость асинхронного ввода/вывода, Райан Дал напоминает об относительном времени доступа к объектам. Доступ к объектам в памяти (порядка наносекунд) производится быстрее, чем к объектам на диске или посети (миллисекунды или секунды). Время доступа к внешним объектам измеряется несметным количеством тактовых циклов и может оказаться вечностью, если клиент, не дождавшись загрузки страницы в течение двух секунд, устанет пялиться на окно браузера и отправится в другое место.

В Node вышеупомянутый запрос следовало бы записать так:


query( SELECT * from db', function (result) {

// произвести какие-то операции с результатом

}):



Разница в том, что теперь результат запроса не возвращается в качестве значения функции, а передается функции обратного вызова, которая будет вызвана позже. Таким образом, возврат в цикл обработки событий происходит почти сразу, и сервер может перейти к обслуживанию других запросов. Одним из таких запросов будет ответ на запрос, отправленный базе данных, и тогда будет вызвана функция обратного вызова. Такая модель быстрого возврата в цикл обработки событий повышает степень использования ресурсов серверов. Это прекрасно для владельца сервера, но еще больший выигрыш получает пользователь, перед которым быстрее открывается содержимое страницы.

В наши дни веб-страницы все чаще собирают данные из десятков источников. Каждому нужно отправить запрос и дождаться ответа на него. Асинхронные запросы позволяют выполнять эти операции параллельно - отправить сразу все запросы, задав для каждого собственный обратный вызов, и, не дожидаясь ответа, вернуться в цикл обработки событий. А когда придет ответ, будет вызвана соответствующая ему функция. Благодаря распараллеливанию данные можно собрать гораздо быстрее, чем, если бы запросы выполнялись синхронно, один за другим. И пользователь по ту сторону браузера счастлив, так как страница загружается быстрее.


.3 Производительность и использование процессора


Своей притягательностью платформа Node отчасти обязана пропускной способности (количество обслуживаемых запросов в секунду). Сравнительные тесты схожих программ, например Apache и Node, показывают фантастический выигрыш в производительности.

Один из популярных эталонных тестов - следующий простой НТТР-сервер, который всего лишь возвращает сообщение «HelloWorld», читаемое из памяти:


var http = require( http');.createServer(function (req, res) {.writeHead(200, {'Content-Type':text/plain'});.end('Hello World\n');

}).listen(8124, "127.0.0.1");.log( Server running at #"justify">Это один из самых простых веб-серверов, которые только можно построить на платформе Node. Объект http инкапсулирует протокол HTTP, а его метод http.createServer создает полнофункциональный веб-сервер, прослушивающий порт, заданный в методе .listen. Каждый запрос (к любому URL любого вида - хоть GET, хоть PUT) приводит к вызову указанной функции. Сервер очень простой и совсем мало «весит». В данном случае вне зависимости от URL возвращается строка «HelloWorld» типа text/plain.

Благодаря такому минимализму это приложение должно демонстрировать максимальную пропускную способность. Поэтому во многих опубликованных исследованиях перечень эталонных тестов начинается с этого простейшего НТТР-сервера.

Райан Дал привел пример простого теста (#"justify">Дастин Маккуэй (Dustin McQuay) (#"justify">PHP/Apache 3187 запросов/с;.js 5569 запросов/с.

Ханнес Вальнёфер (Hannes Wallnflfer), автор RingoJS, написал в своем блоге заметку, в которой предостерегает от принятия важных решений на основе сравнительных тестов (#"justify">Микито Такада (Mikito Takada) рассказал в блоге о сравнительном тестировании производительности Node и Django на примере написанного им приложения «48 hour hackathon» (#"justify">Чтобы в полной мере раскрыть потенциальные возможности Node, крайне важно быстро возвращать управление в цикл обработки событий. Заметим, что если обработчик обратного вызова выполняется «слишком долго», то Node перестает быть тем сверхбыстрым сервером, каким был задуман. В одной из ранних статей о проекте Node (#"justify">В случае небольших веб-приложений («масштабирование в малом») реализация на Node, а не на языках 'Р' (Perl, РНР, Python и т.д.), должна давать выигрыш в производительности. JavaScript - мощный язык, а среда Node со своей современной быстрой виртуальной машиной имеет преимущества в части производительности и организации параллелизма по сравнению с интерпретируемыми языками типа РНР.

Далее Пэйн говорит, что «масштабирование в большом», то есть создание приложений корпоративного уровня, всегда будет трудным и сложным делом. Чтобы обслужить огромное количество пользователей по всему миру, обеспечив при этом необходимую скорость загрузки страниц, обычно приходится включать в систему балансировщики нагрузки, кэширующие серверы, многочисленные резервные машины, расположенные в территориально разнесенных точках. Поэтому платформа разработки, наверное, не так важна, как система в целом.

Мы не узнаем, насколько хороша платформа Node в действительности, пока не увидим пример долгосрочного развертывания в крупных производственных средах.


.4 Использование серверов, экономия затрат и экологичный Интернет


Смысл борьбы за максимальную эффективность (увеличение числа обрабатываемых запросов в секунду) - не только в том, чтобы получить удовольствие от хорошо проделанной технической работы. Имеются также реальные плюсы с точки зрения бизнеса и окружающей среды. Присущая Node способность обрабатывать больше запросов в секунду означает, что можно приобрести меньше серверов. То есть сделать больше меньшими средствами.

Грубо говоря, чем больше серверов, тем выше затраты и тем сильнее воздействие на окружающую среду, и наоборот. Существует целая наука о сокращении затрат и вреда окружающей среде, причиняемого инфраструктурой веб-серверов, и эта грубая рекомендация не может охватить всех деталей. Но общая цель очевидна - сократить количество серверов, снизить затраты и уменьшить ущерб, наносимый окружающей среде.

В опубликованной корпорацией Intel статье «Increasing Data Center Efficiency with Server Power Measurements» (#"justify">final.pdf) приводится методика оценки эффективности и стоимости центров обработки данных. Следует учитывать много факторов, в том числе конструкцию здания, систему охлаждения и проект вычислительной системы. Их эффективная реализация (эффективность ЦОД, плотность ЦОД и плотность СХД) может сократить затраты и вред окружающей среде. Но все это можно свести на нет, развернув неэффективную программную систему, которая заставляет приобретать больше серверов. И наоборот, эффективная программная система позволяет усилить преимущества эффективной организации ЦОД.



2. Характеристики NODE


Перед тем как начать работу с Node, необходимо изучить среду разработки.


.1 Системные требования

лучше всего работает в операционных системах, совместимых со стандартом POSIX. Это различные клоны UNIX (Solaris и т. п.), а также UNIX-подобные системы (Linux, Mac OS Х и т.п.). На самом деле многие встроенные в Node функции - прямые интерфейсы к системным вызовам, описанным в POSIX.

Во многих зрелых языковых платформах (таких как Perl или Python) уже сформировался стабильный набор средств и API, который включается в дистрибутивы операционных систем. Но Node все еще быстро развивается, поэтому включать в дистрибутивы ОС готовые двоичные сборки было бы преждевременно. Следовательно, предпочтительный метод установки Node - сборка из исходного кода. Для этого необходим компилятор языка С (например, GCC) и Python 2.4 (или более поздняя версия). Если использовать в сетевом коде шифрование, то понадобится еще криптографическая библиотека OpenSSL. В современных клонах UNIX эти средства почти всегда включаются в дистрибутив, а конфигурационный скрипт Node обнаруживает их присутствие.

Хотя ОС Windows не совместима с POSIX, Node можно установить на нее, пользуясь POSIX-совместимыми средами (в версии Node 0.4.x и более ранних). Начиная с версии 0.6.x, разработчики Node намереваются обеспечить возможность сборки с помощью естественных для Windows средств.


2.2 Запуск Node-серверов на этапе инициализации системы


Существуют общепринятые способы запуска серверных процессов, свои в каждой операционной системе. Node-сервер запускается так же, как любой другой фоновый процесс (sshd, apache, MySQL и т.д.), например, с помощью скриптов запуска и останова.

В проект Node не включены скрипты запуска и останова для всех операционных систем. Это, пожалуй, правильно: дистрибутив Node - не место для таких скриптов. Считается, что они должны быть частью серверных приложений для Node. Традиционно инициализацией системы ведал демон init, который управляет фоновыми процессами с помощью скриптов, находящихся в каталоге /etc/init.d. В дистрибутивах Fedora и Redhat этот процесс все еще существует, а в других системах применяются иные менеджеры демонов, например Upstart или launchd.

Написание скриптов запуска и останова - только часть дела. Веб-серверы должны быть надежными (например, автоматически перезапускаться после сбоя), легко управляемыми (хорошо интегрироваться с принятой практикой администрирования системы), допускающими наблюдение (сохранять все, что выведено на STDOUT в журналы) и т.д. Node - это, скорее, конструктор, включающий детали для построения серверов, а не готовый, законченный сервер. Для реализации настоящего веб-сервера на платформе Node необходимо написать скрипты для интеграции со средствами управления фоновыми процессами, имеющимися в ОС, для ведения журналов, для обеспечения защиты от вредоносных клиентов, например DoS-атак, и многое другое.

Ниже перечислены инструменты и методики интегрирования Node-серверов со средствами управления фоновыми процессами в нескольких операционных системах, позволяющие гарантировать непрерывное присутствие сервера, начиная с момента инициализации системы:autorestart (http://github.com/shimondoodkin/nodejs-autorestart) -управление экземпляром Node в тех дистрибутивах Linux, где используется Upstart (Ubuntu, Debian и др.);(http://github.com/pgte/fugue) наблюдает за Node-сервером и перезапускает его после сбоя;(http://github.com/indexzero/forever) -небольшая командная утилита Node, которая гарантирует, что скрипт будет работать «вечно». О том, что такое «вечно», Чарли Роббинс (CharlieRobbins) написал статью в блоге(#"justify">средство Upstart для Ubuntu (#"justify">в Mac OS X необходимо написать скрипт для launchd. Apple разместила руководство по созданию launchd-скриптов по адресу #"justify">.3 Использование всех процессорных ядер в многоядерной системе

- однопоточный движок JavaScript. Для браузера Chrome этого достаточно, однако означает, что Node-сервер, работающий на только что купленном 16-ядерном сервере, задействует всего одно ядро, а 15 остальных простаивают.

Однопоточный процесс использует только одно процессорное ядро. Это факт, от которого никуда не уйти. Чтобы задействовать в одном процессе несколько ядер, необходима многопоточная программа. Однако принятая в Node парадигма проектирования без потоков, хотя и позволяет упростить модель программирования, одновременно означает, что Node не использует несколько ядер.

Есть несколько проектов, посвященных разработке многопроцессных конфигураций Node для повышения надежности и задействования всех имеющихся процессорных ядер.

Основная идея состоит в том, чтобы запустить несколько процессов Node и распределять между ними поступающие запросы. Имея кластер из однопоточных процессов, вы сможете использовать все ядра.

Один из таких проектов называется Cluster (http://github.com/LearnBoost/cluster), авторы описывают его как «расширяемый менеджер многоядерных серверов для Node.js». Он запускает конфигурируемый набор дочерних процессов, перезапускает их после сбоя и располагает богатыми средствами протоколирования, управления из командной строки и сбора статистики. Более ранний проект Spark закрылся, уступив место Cluster.



3. Модули Node


Модули и пакеты - это строительные блоки, позволяющие разбить приложение на более мелкие части.

На реализацию модулей Node оказала большое влияние спецификация модулей CommonJS, хотя есть и отличия. Но эти отличия проявляются, только если писать общий код для Node и других систем, основанных на CommonJS. Беглый взгляд на спецификацию Modules/1.1.1показывает, что различия второстепенные.


.1 Как Node ищет модули, затребованные в require('module')?


В Node модули хранятся в файлах, в каждом файле по одному модулю. Есть несколько подходов к именованию модулей и к размещению их в файловой системе. В целом получается весьма гибкая система, особенно в сочетании с npm, стандартным менеджером пакетов для Node.

Идентификаторы модулей и пути

Вообще говоря, имя модуля - это путь, но без расширения имени файла. Таким образом, когда мы пишем require('./simple'), Node знает, что необходимо добавить к имени файла расширение .js и загрузить файл simple.js.

Естественно, ожидается, что файлы, имена которых заканчиваются на .js, содержат код, написанный на JavaScript. Node поддерживает также модули в виде двоичных платформенных библиотек. В таком случае имя файла должно оканчиваться расширением .node.

Некоторые модули Node не являются файлами в файловой системе, а «зашиты» в исполняемый файл Node. Это модули ядра (Core), документированные на сайте nodejs.org. Изначально они существуют в виде файлов в дереве исходного кода Node, но в ходе сборки прикомпилируются к исполняемому файлу.

Существуют три типа идентификаторов модулей: относительные, абсолютные и верхнего уровня.

Относительные идентификаторы модулей начинаются строкой "./" или "../", а абсолютные - строкой "/". Здесь имеется полная аналогия с семантикой файловой системы, совместимой с POSIX, в которой пути записываются относительно исполняемого файла.

Понятно, что абсолютные идентификаторы модулей записываются относительно корня файловой системы.

В начале идентификатора модуля верхнего уровня нет ни ".", ни "..", ни "/", это просто имя модуля. Такие модули хранятся в одном из нескольких предопределенных каталогов, например node_modules, или в каталогах, перечисленных в массиве require.paths.


.2 Локальные модули внутри приложения


Все множество мыслимых модулей можно разбить на две категории: являющиеся и не являющиеся частью приложения. Модули, не являющиеся частью конкретного приложения, написаны, имея в виду какую-то обобщенную цель. Начнем с реализации модулей, используемых только в вашем приложении.

В типичном приложении модули распределены по нескольким каталогам. Они хранятся в системе управления версиями и впоследствии копируются на серверы. Эти модули знают относительные пути к своим «братьям» и могут использовать эту информацию, чтобы ссылаться друг на друга, но относительным идентификаторам.

Чтобы лучше понять, как это организовано, возьмем в качестве примера структуру одного из пакетов для Node, каркаса разработки веб-приложений Express. Он состоит из нескольких модулей, организованных в виде иерархии, которую разработчики Express находят полезной. Подобные иерархии имеет смысл создавать, когда приложение достигает определенного уровня сложности, оправдывающего его разбиение на части, большие, чем модуль, но меньшие, чем приложение. К сожалению, специального термина для таких структурных компонентов в Node нет, поэтому приходится употреблять корявую фразу «разбивать на части, большие, чем модуль». Каждая такая часть представляется каталогом с несколькими модулями.


.3 Комплектация приложения с внешними зависимостями


Для включения модулей, находящихся в каталоге node_modules, употребляется идентификатор верхнего уровня:

express = require('express');

производит поиск модулей во всех каталогах node_modules, а их существует несколько. Алгоритм начинает поиск в каталоге текущего модуля, потом добавляет в путь node_modules и ищет там. Если модуль не найден в каталоге node_modules, то Node переходит к родительскому каталогу и пробует еще раз - и так до тех пор, пока не дойдет до корня файловой системы.

Но что, если мы захотим использовать каркас Express в своем приложении? Ничего сложного, просто создаем каталог node_modules внутри дерева своего приложения и устанавливаем туда Express:




Здесь показано гипотетическое приложение drawapp. Если каталог node_modules расположен так, как показано на рисунке, то любой модуль внутри drawapp может получить доступ к express следующим образом:

express = require( express');


Однако те же самые модули не смогут добраться до модуля qs, скрытого внутри каталога node_modules, являющегося частью самого каркаса Express. Просмотр каталогов node_modules, содержащих искомый модуль, производится вверх по иерархии файловой системы, без захода в дочерние каталоги.

Аналогично: если установить модуль в каталог lib/node_modules, то он будет доступен из draw.js и svg.js, но недоступен из index.js. Как и раньше, поиск происходит вверх от текущего каталога, а не вглубь него.

При обходе каталогов node_modules Node останавливается, как только найдет искомый модуль. Так, если ссылка встречается в файле draw,js или svg.js, то будут просмотрены следующие каталоги:


/home/david/projects/drawapp/lib/node_modules

/home/david/projects/drawapp/node_modules

/home/david/projects/node_modules

/home/david/node_modules

/home/node_modules

/node_modules


Каталог node_modules играет важнейшую роль, позволяя системе управления пакетами выпутаться из лабиринта конфликтующих версий. Вместо того чтобы помещать все модули в одно место и медленно сходить с ума, пытаясь разрешить зависимости от конфликтующих номеров версий, мы можем завести несколько каталогов node_modules и при необходимости складывать конкретные версии в конкретное место. Разные версии одного модуля могут находиться в разных каталогах node_modules, и при условии, что эти каталоги расположены правильно относительно друг друга, никаких конфликтов не возникнет.

Пусть, например, мы написали приложение, в котором используется модуль forms (http://github.com/caolan/forms) для построения форм, и, когда у вас уже накопились сотни форм, авторы модуля внесли в него несовместимые изменения. Переделывать и заново тестировать все формы сразу вам не хочется, лучше делать это постепенно. Для этого надо будет создать в приложении два каталога, организовать в каждом свой подкаталог node_modules и поместить в них разные версии модуля forms. Затем, по мере перевода очередной формы на новый модуль forms, ее код перемещается в каталог, где находится новая версия.


.4 Системные модули в каталогах, перечисленных в массиве require.paths


При поиске каталогов node_modules Node не ограничивается деревом приложения. Алгоритм доходит до корня файловой системы, поэтому можно создать каталог /node_modules и организовать в нем глобальный репозиторий модулей. Именно здесь будет завершаться поиск модуля, не найденного ни в каком другом месте.

Но Node предоставляет и еще один механизм, основанный на переменной require,paths. Это массив имен каталогов, в которых следует искать модули.

Приведем пример:


$ node

>require.paths;

["/home/david/.node_modules",''/home/david/.node_libraries", "/usr/local/lib/node"]

Для заполнения массива require,paths используется переменная окружения NODE_PATH:

$ export NODE_PATH=/usr/lib/node

$node

>require.paths;

["/usr/lib/node","/home/david/.node_libraries","/usr/local/lib/node]

>


Раньше в программах для Node часто применялась следующая идиома для добавления новых элементов в массив require.paths:require.paths.push(__dirname). Однако теперь она не рекомендуется, потому что, как выяснилось, является источником путаницы. Хотя так делать можно и даже еще остались модули, в которых эта идиома встречается, но смотрят на ее использование с большим неодобрением. Если несколько модулей помещают каталоги в require.paths, то результаты непредсказуемы.

В большинстве случаев рекомендуется устанавливать модули в каталоги node_modules.

Составные модули - модули-каталоги

Составной модуль может включать несколько внутренних модулей, файлы данных, файлы шаблонов, документацию, тесты и прочее. Все это можно поместить в хорошо продуманную структуру каталогов, которую Node будет рассматривать как модуль, и загружать командой require('moduleName'). Для этого следует добавить в каталог файл модуля index.js или файл с именем package.json. Файл package.json должен содержать данные, описывающие модуль, в формате, очень похожем на формат файла package.json, используемого менеджером пакетов npm (см. ниже). В обоих случаях для совместимости с Node достаточно очень небольшого набора полей, распознаваемых npm.

Точнее, Node распознает следующие поля в файле package.json:


{ name: "myAwesomeLibrary",: "./lib/awesome.js" }


При таком файле package.json команда require('myAwesomeLibrary') найдет этот каталог и загрузит файл


/path/to/node_modules/myAwesomeLibrary/lib/awesome.js


Если файла package.json нет, то Node будет вместо него искать файл index.js, то есть загрузит файл:


/path/to/node_modules/myAwesomeLibrary/index.js


В любом случае (index.js или package.json) реализовать составной модуль, содержащий внутренние модули и другие файлы, несложно. Если вернуться к рассмотренной выше структуре пакета Express, то мы увидим, что некоторые модули пользуются относительными идентификаторами для ссылки на другие модули в пакете, а для включения модулей, разработанных кем-то другим, можно воспользоваться каталогом node_modules.

Менеджер пакетов для Node (npm)- это система управления и распространения пакетов для Node, ставшая стандартом де-факто. Концептуально она похожа на такие инструменты, как apt-get (Debian), rpm/yum (Redhat/Fedora), MacPorts (Mac OS X), CPAN (Perl) и PEAR (PHP). Ее задача - обеспечить публикацию и распространение пакетов Node через Интернет с помощью простого интерфейса командной строки. Npm позволяет быстро находить пакеты для решения конкретной задачи, загружать и устанавливать их, а также управлять уже установленными пакетами.

В npm определен формат пакета для Node, основанный на спецификации CommonJS.

Формат npm-пакетапакет представляет собой структуру каталогов, описанную в файле package.json. Именно так мы выше определили составной модуль, отличие только в том, что npm распознает значительно больше полей, чем Node. Исходной точкой для определения формата package,json для npm послужила спецификация CommonJS Packages/1.0. Получить документацию по структуре файла package,json позволяет следующая команда:


$ npm help json


Простейший файл package,json выглядит следующим образом:


{ name: "packageName",: "1.0",: "mainModuleName",: {

"mod1: "lib/mod1",

"mod2": "lib/mod2"

}

}


Файл представлен в формате JSON, c. которым вы как программист на JavaScript, должно быть, встречались уже сотни раз.

Наиболее важны поля name и version. Значение name подставляется в URL-адреса и названия команд, поэтому выбирать его следует с учетом безопасности в этих контекстах. Если мы соберемся опубликовать пакет в общедоступном репозитории npm-пакетов, то должны проверить, не занято ли выбранное имя. Для этого можно обратиться к сайту #"justify">$ npm search packageName


Поле main служит той же цели, что и в составных модулях (см. предыдущий раздел). Оно ссылается на модуль, который следует загружать при вызове функции require('packageName'). Пакеты могут содержать много модулей, и все их можно перечислить в списке модулей.

Пакеты можно упаковывать в tgz-архивы, что особенно удобно для распространения через Интернет.

Пакет может объявлять зависимости от других пакетов. Именно благодаря этой возможности npm способен автоматически устанавливать модули, необходимые тому, который устанавливается явно. Зависимости объявляются следующим образом:


"dependencies":

{ "foo" : "1.0.0 - 2.9999.9999"

, "bar" : ">=1.0.2 <2.1.2"

}

Людям будет проще найти пакет в npm-репозитории (#"justify">"description: "My wonderful packages walks dogs",

"homepage": "#"justify">"author": [email protected]


В состав некоторых npm-пакетов входят исполняемые программы, которые должны быть установлены в каталог, упомянутый в переменной PATH для данного пользователя. Они объявляются с помощью поля bin. Это словарь, отображающий имена команд на имена реализующих их скриптов. Командные скрипты устанавливаются в каталог, содержащий исполняемый файл node, под указанным именем.


bin: {

'nodeload.js': './nodeload.js , 'nl.js': './nl.js'

}


В поле directories документируется структура каталогов пакета. Каталог lib автоматически просматривается при поиске загружаемых модулей. Существуют также поля для двоичных файлов, страниц руководства и документации.


directories: { lib: './lib', bin: './bin' },


В поле scripts перечисляются скриптовые команды, запускаемые на различных этапах жизненного цикла пакета, а именно: установка, активация, удаление, обновление и др. Для получения дополнительных сведений о скриптовых командах нужно ввести следующую команду:


$ npm help scripts


Это лишь краткое введение в формат npm-пакетов, для получения полной документации необходимо набрать npm help json.

Версии и диапазоны версий пакетаничего не знает о номерах версий. Она знает о модулях и может интерпретировать структуру каталогов так, будто это модуль. В Node имеется развитая система поиска модулей, но номера версий в ней не учитываются. Однако о номерах версий знает npm. Он применяет модель семантической версионности (см. ниже) и, как мы видели, может устанавливать модули через Интернет, искать устаревшие модули и обновлять их. Все эти операции зависят от версий, поэтому познакомимся поближе с тем, как npm обрабатывает номера и метки версий.

Ранее мы использовали команду npm list для вывода списка установленных пакетов, и в этом списке отображались также номера версий. Если же требуется узнать только номер версии конкретного модуля, то подойдет следующая команда:


$ npm view express version


2.4.0

Во всех случаях, когда команда npm принимает имя пакета, вы можете дописать в конец имени номер или метку версии. Это позволяет работать с конкретной версией пакета. Например, если вы протестировали приложение с определенной версией пакета в тестовой среде, то можете установить ту же версию и в производственной:


$ npm install [email protected]@1.2.2 ./node_modules/express/node_modules/[email protected] ./node_modules/express/node_modules/[email protected] ./node_modules/express/node_modules/qs

[email protected] ./node_modules/express


В npm есть концепция метки (tag), которой можно воспользоваться, чтобы установить самую свежую стабильную версию пакета:


$ npm install sax@stable


Имена меток произвольны и необязательны. Их выбирает автор пакета, и не во всех пакетах они применяются.позволяет просмотреть зависимости от других пакетов, хранящиеся в файле package:


$ npm view mongoose dependencies

{ hooks: '0.1.9' }

$ npm view express dependencies

{ connect: >= 1.5.1 < 2.0.0',: '>= 0.0.1',: '>= 0.0.6 }


Зависимости - это механизм, с помощью которого npm узнает, какие еще модули необходимо установить. Во время установки модуля npm смотрит, от чего он зависит, и устанавливает те модули, которые не были установлены ранее.

Хотя эта система проста и понятна всякому, кто хоть раз имел дело с программным обеспечением, за ней стоит строгая модель. Реализуя систему нумерации версий, автор npm пользовался спецификацией семантической версионности, опубликованной на сайте #"justify">Версии представляются строками вида X.Y.Z, где X, Y и Z обычно являются целыми числами; X - основной номер, Y - дополнительный номер, Z - номер исправления (например, 1.2.3).

После номера исправления в строке может указываться произвольный текст, описывающий так называемые «специальные версии» (например, 1.2.3betal).

При сравнении номеров версий сравниваются не строки, а числа X, Y и Z. Например, 1.9.0 < 1.10.0 < 1.11.3. и 1.0.0betal < 1.0.0beta2 < 1.0.0.

Совместимость документируется с помощью следующих соглашений о нумерации версий:

пакеты с основным номером версии 0 (X = 0) совершенно нестабильны, их API может измениться в любое время;

если изменение исправляет только ошибки и гарантирует обратную совместимость, то следует увеличивать номер исправления (Z);

дополнительный номер версии (Y) нужно увеличивать при добавлении функциональности, сохраняющей обратную совместимость (например, добавлена новая функция, а все прочие обратно совместимы);

основной номер версии (X) следует увеличивать при внесении несовместимых изменений.



4. Хранение и выборка данных


Рассмотрим имеющиеся в Node методы хранения данных. Каким бы мощным ни был веб-каркас Express, без умения сохранять данные от него мало толку. Обычно данные сохраняют в какой-нибудь базе данных. Сегодня имеются технологии баз данных на самые разные случаи - традиционные хранилища на основе SQL, документо-ориентированные базы данных без использования SQL, простые хранилища ключей и значений или веб-службы запросов типа YQL.


.1 Движки сохранения данных для Node


В Node не предусмотрена встроенная поддержка какой-нибудь системы хранения данных, если не считать чтение и запись в файловую систему. Для работы с системами хранения, в частности с базами данных, необходимо использовать соответствующий модуль. На вики-сайте Node перечислено два десятка таких модулей для работы с CouchDB, MongoDB, MySQL, Postgres, SQLite3, Memcache, REDIS, YQL и другими системами. См. http://github.com/joyent/node/wiki/modules#database.

В общем случае необходимо установить как сам модуль, так и его зависимости, в том числе платформенный код клиентских библиотек конкретной СУБД. Например, модулям для работы с MySQL необходимы сервер MySQL и соответствующая клиентская библиотека.


.2 SQLite3 - облегченная встраиваемая база данных на основе SQL


СУБД на основе SQL необязательно подразумевает наличие тяжеловесного сервера и высокооплачиваемых администраторов баз данных. Установить SQLite3 (#"justify">.3 Mongoose - интерфейс между Node и MongoDB

- одна из лидирующих «nosql» СУБД (nosql означает, что она не основана на языке SQL). В описании говорится, что это «масштабируемая, высокопроизводительная, документо-ориентированная СУБД с открытым исходным кодом». Она позволяет хранить документы в формате, близком к JSON, без строго определенной схемы, и обладает целым рядом передовых возможностей. Дополнительные сведения и документацию можно найти на сайте проекта #"justify">5. Практический пример на основе продолжительных вычислений (числа Фибоначчи)


Приложение Math Wizard наглядно демонстрирует основную проблему приложений для Node. Если какая-нибудь функция обратного вызова долго не возвращает управление циклу обработки событий, то все приложение перестает отвечать на запросы.

Чтобы убедиться в этом, необходимо зайти на страницу вычисления чисел Фибоначчи и ввести «большое» число, например 50. На его вычисление уйдет ОЧЕНЬ много времени (часы или даже дни), при этом процесс Node будет потреблять почти все процессорное время, и ни в каком другом окне браузера получить ответ от приложения Math Wizard будет невозможно. А все потому, что наш алгоритм нахождения последовательности чисел Фибоначчи выполняет очень большой объем вычислений. А почему не отвечает браузер? Потому что пока программа занимается вычислениями, она не дает работать циклу обработки событий, и, значит, Node не может ответить на запросы.

Поскольку в Node имеется всего один поток выполнения, для обработки запросов необходимо, чтобы обработчики быстро возвращали управления в цикл обработки событий. Обычная практика написания асинхронных программ предполагает, что возврат в этот цикл производится быстро. Это справедливо даже в случае, когда для обработки запроса нужно загружать данные с сервера на другом конце земного шара, поскольку вследствие неблокирующего ввода/вывода управление быстро возвращается в цикл. Но наша наивная функция вычисления чисел Фибоначчи не удовлетворяет этому условию, так как выполняет длительную блокирующую операцию. В таком случае система не может обрабатывать новые запросы, и Node перестает быть тем, чем задумана, - суперскоростным веб-сервером.

В данном примере проблема очевидна. Время обработки возрастает так быстро, что уже для вычисления числа Фибоначчи, отстоящего сравнительно недалеко от начала последовательности, его уходит столько, что вполне можно успеть съездить в отпуск на Тибет. Но в приложении замедление реакции может быть не так заметно, и как же тогда узнать, какие запросы обрабатываются слишком долго? Один из способов - воспользоваться каким-нибудь встраиваемым в браузер средством измерения задержки, например YSlow. Эвристическое правило простое: если браузером пользуется человек, то на загрузку страницы должно уходить не больше двух секунд, иначе вы рискуете потерять посетителя.

В Node существуют два общих способа решения этой проблемы.

Переработка алгоритма. Например, выбранный нами алгоритм вычисления чисел Фибоначчи не оптимален, его можно заменить более быстрым. А если это не получается, то нужно разбить алгоритм на кусочки и поручить управление ими циклу обработки событий. Один пример такого подхода мы рассмотрим ниже.

Создание фоновой службы. Можно себе представить специальный вспомогательный сервер, который занимается только вычислением чисел Фибоначчи? Пожалуй, нет, но, вообще говоря, выделение серверов заднего плана для разгрузки серверов переднего плана - обычное дело. Обработчик запросов должен будет асинхронно обращаться к службам или базам данных, собирать информацию, необходимую для ответа, и, когда все будет готово, отправить ответ браузеру.

Мы могли бы оптимизировать алгоритм вычисления чисел Фибоначчи, но вместо этого преобразуем нашу неасинхронную функцию в асинхронную, с обратным вызовом. Асинхронное вычисление чисел Фибоначчи - не самая лучшая мысль, но зато на этом примере мы сможем продемонстрировать переработку алгоритма. Мы разобьем вычисление на последовательность обратных вызовов, которые будут вызываться из цикла обработки событий.

Первым делом добавим новую функцию вычисления чисел Фибоначчи вместо первоначального наивного варианта. С вами такое тоже может случиться - первая попытка оказывается неудачной и медленной, но впоследствии находится более подходящая реализация. Добавьте в файл math.js такой код:


var fibonacciAsync = exports.fibonacciAsync = function(n, done) {(n === 1 || n === 2)(1);{.nextTick(function() {(n-1, function(val1) {.nextTick(function() {(n-2, function(val2) {(val1+val2);

});

});

});

});

}

}


Это новый асинхронный алгоритм. Мы преобразовали простую функцию в асинхронно управляемое вычисление, результат которого передается с помощью функции обратного вызова следующим образом:


fibonacciAsync(n, function(value) {

// произвести какие-то действия с value

});


Мы вызываем функцию process.nextTick, чтобы превратить рекурсивную функцию в последовательность шагов, вызываемых диспетчером цикла обработки событий. Очередной шаг реализован в виде обратного вызова из цикла, а возврат в цикл после каждого шага производится быстро, так что сервер может продолжать обработку HTTP-запросов. Это не единственный способ разбить алгоритм на шаги, вызываемые посредством цикла обработки событий. Для решения данной задачи предназначен модуль async, в котором есть много функций, призванных укротить асинхронный JavaScript.

В коде fibonacciAsync функция process.nextTick заменяет следующее предложение в первоначальном варианте алгоритма:


return fibonacci(n-1)+fibonacci(n-2);


Задача состоит в том, чтобы вычислить два числа Фибоначчи, сложить их и вернуть результат вызывающей функции. В новом алгоритме каждому из трех шагов соответствует анонимная функция. А чтобы их вызовы производились из цикла обработки событий, мы использовали process.nextTick.

Прежде чем двигаться дальше, надо осмыслить это решение. Мы никак не уменьшили объем вычислений, а лишь распределили его по циклу обработки событий. Процесс Node по-прежнему нагружает процессор, поэтому выбранный нами подход - не лучший способ оптимизировать такой алгоритм, как вычисление чисел Фибоначчи. Но он демонстрирует распределение работы с помощью цикла обработки событий; иногда эта техника полезна, иногда - не очень.

Создаем новый файл fibo2-node.js и меняем app-node.js, включив вызов require('./fibo2-node'), чтобы использовался новый модуль вычисления чисел Фибоначчи. Эта строка уже есть в файле app-node.js, только закомментирована. Вам остается лишь перенести комментарий с одной строки на другую:


var htutil = require('./htutil');math = require('./math');sendResult(req, res, a, fiboval) {.writeHead(200, {

'Content-Type': 'text/html'

});.end(.page("Fibonacci", htutil. navbar(), [

(!isNaN(fiboval) ?

("<p>result>fibonacci {a} = {fibo}</p>

.replace("{a}", a)

.replace("{fibo}", fiboval))

: ""),

"<p>Enter a number to see its fibonacci</p>",

"<form name='fibonacci' action='/fibonacci' method='get'>",

"A: <input type='text' name='a' />",

"</form>"

].join( "\n"))

):

}.get = function(req, res) {(!isNaN(req.a)) {.fibonacciAsync(Math.floor(req.a), function(val) {(req, res, Math.floor(req.a), val);

});

} else {(req, res, NaN, NaN);

}

}


Мы вынесли всю работу в функцию sendResult, которая вызывается по-разному в зависимости от того, задал пользователь номер числа Фибоначчи или нет.



Для вычисления больших чисел Фибоначчи все равно требуется много времени, но сервер не блокируется и может обрабатывать другие запросы. В этом легко убедиться, открыв страницу в нескольких вкладках браузера. В одной вкладке запросив вычисление большого числа Фибоначчи, а во второй выполнив другие запросы. Теперь сервер не впадает в глубокую задумчивость, а возвращает результаты.



Заключение


Этот проект - прекрасная отправная точка, с которой можно начать путешествие в увлекательный мир разработки веб-приложений для Node.js. При желании на практике можно научиться пользоваться серверным и клиентским объектами HTTP, каркасами Connect и Express, освоить алгоритмы асинхронного выполнения и узнать, как работать с базами данных на основе SQL и с MongoDB. А Также познакомиться с применяемой в Node.js системой организации модулей на основе спецификации CommonJS, которая позволяет реализовать представительное подмножество технологии объектно-ориентированного проектирования.



Список использованной литературы


1. Хэррон Д. «Node.js. Разработка серверных веб-приложений в JavaScript»: Пер. с англ. Слинкина А.А. - М.: ДМК Пресс, 2012. - 144с.: ил.


Министерство образования и науки РФ Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования "Рязанский

Больше работ по теме:

КОНТАКТНЫЙ EMAIL: [email protected]

Скачать реферат © 2017 | Пользовательское соглашение

Скачать      Реферат

ПРОФЕССИОНАЛЬНАЯ ПОМОЩЬ СТУДЕНТАМ