Применение Node.js в качестве полной среды разработки для облачных решений

Описание параллельной модели, использующей асинхронный ввод/вывод посредством обратных вызовов, и построение учебного чат-сервера

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

Ной Гифт, разработчик программного обеспечения, IBM  

Photo of Noah GIftНой Гифт (Noah Gift) – соавтор книги "Python For Unix and Linux" издательства O'Reilly. Он также является автором, докладчиком, консультантом и лидером сообщества, пишущим публикации для IBM developerWorks, Red Hat Magazine, O'Reilly и MacTech. Адрес сайта его консалтинговой компании - www.giftcs.com, адрес его персонального сайта - www.noahgift.com. В настоящий момент Ной также поддерживает сайт www.pyatl.org – место общения пользователей, работающих с Python, из Атланты, Джорджия. Он окончил университет штата Калифорния, получив степень магистра в области информационных компьютерных систем, а также окончил политехнический университет штата Калифорния, расположенный в Сан-Луис-Обиспо, получив степень бакалавра в области пищевых наук. Он также является сертифицированным системным администратором Apple и LPI, имеет опыт работы в таких компаниях, как Caltech, Disney Feature Animation, Sony Imageworks и Turner Studios. Все свое свободное время он проводит со своей женой Лией (Leah) и их сыном Лиамом (Liam), играя на пианино и духовно совершенствуясь.



Джереми Джонс, старший инженер по программному обеспечению, Predictix

Jeremy JonesДжереми Джонс (Jeremy Jones) — старший системный инженер в компании Predictix. Он является автором многочисленных онлайновых статей, а также соавтором книги Python for UNIX and Linux System Administration. Чаще всего он решает проблемы с помощью языка Python, однако он также владеет такими инструментами, как bash, JavaScript, perl, C# и Java. Он любит выявлять и устранять неочевидные проблемы в различных областях. Свое свободное время Дж. Джонс посвящает изготовлению различных предметов из дерева, а также общению с семьей.



22.10.2012

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

Вместо того чтобы преодолевать трудности с языком JavaScript, каркас Node.js использует его в качестве полного стека средств разработки — от кода на стороне сервера до браузера. Node.js воплощает еще одну инновационную идею: параллельная модель с асинхронным вводом/выводом, реализованным посредством обратных вызовов.

Облачные платформы Node.js

Существенная выгода от использования инфраструктуры Node.js возникает в случае, когда это делается в облачной среде. Для разработчиков приложений эта выгода, как правило, сводится к использованию модели «платформа как сервис» (Platform as a Service, PaaS) или «инфраструктура как сервис» (Infrastructure as a Service, IaaS). С точки зрения разработчика наиболее формализованный и, возможно, самый удобный подход состоит в использовании поставщика PaaS-сервисов. На рис. 1 показано весьма упрощенное структурное представление моделей PaaS и IaaS.

Рисунок 1. Структуры моделей PaaS и IaaS
The PaaS and IaaS structures

Недавно в рамках впечатляющего проекта с открытым исходным кодом под названием Cloud Foundry был выпущен код для создания частных PaaS-облаков, способных исполнять код Node.js. Аналогичный механизм хостинга, способный, помимо прочего, принимать исправления программного обеспечения, доступен в публичных и коммерческих облаках.

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


Использование оболочки Node.js

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

Чтобы в интерактивном режиме написать JavaScript-функцию в Node.js, в командной строке введите node, как показано ниже:

lion% node
> var foo = {bar: 'baz'};
> console.log(foo);
{ bar: 'baz' }
>

В этом примере был создан объект foo, который затем был выведен на консоль посредством вызова console.log. Это было очень убедительно и функционально, однако настоящая функциональность начинается, когда вы задействуете для исследования foo автодополнение с помощью табуляции, как в следующем примере. Если вы наберете на клавиатуре foo.bar., а затем нажмете на клавишу табуляции, вы увидите методы, доступные для этого объекта.

> foo.bar.
[...output suppressed for space...]
foo.bar.toUpperCase           foo.bar.trim
foo.bar.trimLeft              foo.bar.trimRight

Метод toUpperCasemethod выглядит достаточно интересным, чтобы его опробовать. Вот что он делает:

> foo.bar.toUpperCase();
'BAZ'

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

После этого простого введения пора переходить к практическому упражнению.


Построение чат-сервера в среде Node.js

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

Чат-сервер — это программа, к которой могут подключаться несколько клиентов. Каждый клиент может писать сообщения, которые затем транслируются всем остальным клиентам (пользователям). Ниже представлен код простейшего чат-сервера.

net = require('net');

var sockets = [];

var s = net.Server(function(socket) {

    sockets.push(socket);

    socket.on('data', function(d) {

        for (var i=0; i < sockets.length; i++ ) {
            sockets[i].write(d);
        }
    });
});

s.listen(8001);

Мы построили работающий чат-сервер из менее чем 20 строк программного кода (а фактически что-либо делают лишь восемь строк). Эта простая программа действует следующим образом:

  • Когда сокет устанавливает соединение, объект socket присоединяется к массиву.
  • Когда клиент «записывает» данные в свое соединение, эти данные записываются во все сокеты.

Теперь мы пройдемся по этому коду и рассмотрим, каким образом в этом примере реализуется базовая функциональность чат-сервера. Первая строка разрешает доступ к содержимому модуля net:

net = require('net');

Используем элемент Server из этого модуля.

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

var sockets = [];

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

var s = net.Server(function(socket) {

Единственным аргументом, который передается в Server, является функция, которая вызывается для каждого соединения клиента. Внутри этой функции соединение клиента нужно добавить к списку всех соединений клиентов:

sockets.push(socket);

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

socket.on('data', function(d) {

    for (var i=0; i < sockets.length; i++ ) {
        sockets[i].write(d);
    }
});

Вызов socket.on() регистрирует обработчика событий с узлом, благодаря чему он знает что делать, когда происходят определенные события. При получении данных от клиента инфраструктура Node.js осуществляет вызов именно этого обработчика событий. Кроме того, имеются следующие обработчики событий: connect, end, timeout, drain, error, close.

По своей структуре вызов socket.on () подобен вызову Server(), упомянутому ранее. Вы передаете функцию в оба этих вызова, а вызов этой функции осуществится, когда что-либо произойдет. Такой подход с обратным вызовом широко распространен в асинхронных сетевых инфраструктурах. Это основной момент, с которым возникают проблемы у специалистов с опытом процедурного программирования, когда они начинают заниматься такими асинхронными инфраструктурами, как Node.js.

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

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

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

net = require('net');

var sockets = [];
var name_map = new Array();
var chuck_quotes = [
    "There used to be a street named after Chuck Norris, but it was changed because 
     nobody crosses Chuck Norris and lives.",
    "Chuck Norris died 20 years ago, Death just hasn't built up the courage to tell 
     him yet.",
    "Chuck Norris has already been to Mars; that's why there are no signs of life.",
    "Some magicians can walk on water, Chuck Norris can swim through land.",
    "Chuck Norris and Superman once fought each other on a bet. The loser had to start 
     wearing his underwear on the outside of his pants."
]

function get_username(socket) {
    var name = socket.remoteAddress;
    for (var k in name_map) {
        if (name_map[k] == socket) {
            name = k;
        }
    }
    return name;
}

function delete_user(socket) {
    var old_name = get_username(socket);
    if (old_name != null) {
        delete(name_map[old_name]);
    }
}

function send_to_all(message, from_socket, ignore_header) {
    username = get_username(from_socket);
    for (var i=0; i < sockets.length; i++ ) {
        if (from_socket != sockets[i]) {
            if (ignore_header) {
                send_to_socket(sockets[i], message);
            }
            else {
                send_to_socket(sockets[i], username + ': ' + message);
            }
        }
    }
}

function send_to_socket(socket, message) {
    socket.write(message + '\n');
}

function execute_command(socket, command, args) {
    if (command == 'identify') {
        delete_user(socket);
        name = args.split(' ', 1)[0];
        name_map[name] = socket;
    }
    if (command == 'me') {
        name = get_username(socket);
        send_to_all('**' + name + '** ' + args, socket, true);
    }
    if (command == 'chuck') {
        var i = Math.floor(Math.random() * chuck_quotes.length);
        send_to_all(chuck_quotes[i], socket, true);
    }
    if (command == 'who') {
        send_to_socket(socket, 'Identified users:');
        for (var name in name_map) {
            send_to_socket(socket, '- ' + name);
        }
    }
}

function send_private_message(socket, recipient_name, message) {
    to_socket = name_map[recipient_name];
    if (! to_socket) {
        send_to_socket(socket, recipient_name + ' is not a valid user');
        return;
    }
    send_to_socket(to_socket, '[ DM ' + get_username(socket) + ' ]: ' + message);
}

var s = net.Server(function(socket) {
    sockets.push(socket);
    socket.on('data', function(d) {
        data = d.toString('utf8').trim();
        // check if it is a command
        var cmd_re = /^\/([a-z]+)[ ]*(.*)/g;
        var dm_re = /^@([a-z]+)[ ]+(.*)/g;
        cmd_match = cmd_re.exec(data)
        dm_match = dm_re.exec(data)
        if (cmd_match) {
            var command = cmd_match[1];
            var args = cmd_match[2];
            execute_command(socket, command, args);
        }
        // check if it is a direct message
        else if (dm_match) {
            var recipient = dm_match[1];
            var message = dm_match[2];
            send_private_message(socket, recipient, message);
        }
        // if none of the above, send to all
        else {
            send_to_all(data, socket);
        };

    });
    socket.on('close', function() {
        sockets.splice(sockets.indexOf(socket), 1);
        delete_user(socket);
    });
});
s.listen(8001);

Более изощренные возможности: чат-сервер с балансировкой нагрузки

Нередко одной из побудительных причин развертывания в облачной среде является необходимость масштабирования по мере увеличения нагрузки. Такое развертывание требует той или иной разновидности механизма для балансировки (выравнивания) нагрузки.

Большинство "легких" веб-серверов, таких как nginx и lighttpd, способны осуществлять балансировку нагрузки между несколькими HTTP-серверами, однако если вам нужно балансировать нагрузки между не-HTTP-серверами, nginx — не самый лучший выбор. Существуют стандартные балансировщики TCP-нагрузки, однако вам может не понравиться алгоритм выравнивания нагрузки, который они используют. Или вам может потребоваться определенная функциональность, которую они не поддерживают. И, наконец, у вас может возникнуть непреодолимое желание написать собственный балансировщик нагрузки — просто ради развлечения.

Рассмотрим простейший балансировщик нагрузки. Он не осуществляет никаких переключений при отказе (failover). Он предполагает, что все места назначения остаются доступными. Кроме того, он не производит никакой обработки ошибок. Это весьма «спартанский» балансировщик. Его основная идея состоит в том, что он принимает сокетное соединение от клиента, по случайному закону выбирает сервер назначения для установки соединения и подключается к нему, а затем передает все данные от клиента на этот сервер и все данные от этого сервера обратно клиенту.

net = require('net');

var destinations = [
    ['localhost', 8001],
    ['localhost', 8002],
    ['localhost', 8003],
]

var s = net.Server(function(client_socket) {
    var i = Math.floor(Math.random() * destinations.length);
    console.log("connecting to " + destinations[i].toString() + "\n");
    var dest_socket = net.Socket();
    dest_socket.connect(destinations[i][1], destinations[i][0]);

    dest_socket.on('data', function(d) {
        client_socket.write(d);
    });
    client_socket.on('data', function(d) {
        dest_socket.write(d);
    });
});
s.listen(9001);

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

Определение Server() подобно примеру с чат-сервером. Мы создаем сокет-сервер и заставляем его осуществлять прослушивание порта. В данном случае он будет прослушивать порт 9001.

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

var i = Math.floor(Math.random() * destinations.length);

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

В этом примере имеются два именованных объекта socket: client_socket и dest_socket.

  • client_socket— это соединение между балансировщиком нагрузки и клиентом.
  • dest_socket— это соединение между балансировщиком нагрузки и балансируемыми серверами.

Каждый из этих двух сокетов обрабатывает одно событие: получение данных. Когда любой из этих сокетов получает данные, он записывает эти данные в другой сокет.

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

  1. Когда клиент соединяется с балансировщиком нагрузки, инфраструктура Node.js создает сокет между клиентом и собой. Мы называем этот сокет client_socket.
  2. После того как соединение будет установлено, балансировщик нагрузки выбирает пункт назначения и создает сокетное соединение с этим пунктом назначения. Мы называем этот сокет dest_socket.
  3. Когда клиент присылает данные, балансировщик нагрузки отсылает эти же данные на сервер назначения.
  4. Когда сервер назначения отвечает и записывает определенные данные в сокет dest_socket, балансировщик нагрузки направляет эти данные обратно клиенту через сокет client_socket.

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


За пределами решений, созданных своими силами: веб-инфраструктура Express

Каркас Node.js располагает определенными возможностями HTTP-сервера, однако эти возможности являются низкоуровневыми. Если вы собираетесь создавать веб-приложения в среде Node.js, можно воспользоваться инфраструктурой Express для разработки веб-приложений, которая была специально создана для платформы Node.js и устраняет некоторые из ее недостатков.

В следующем примере мы сосредоточим внимание на нескольких очевидных преимуществах использования Express по сравнению с "чистой" инфраструктурой Node.js. Одним из таких преимуществ является маршрутизация запросов. Другое преимущество — регистрация событий для HTTP-глаголов, таких как get или post.

Ниже приведено очень простое веб-приложение. Оно не делает ничего, кроме демонстрации некоторых базовых возможностей инфраструктуры Express.

var app = require('express').createServer();

app.get('/', function(req, res){
  res.send('This is the root.');
});

app.get('/root/:id', function(req, res){
  res.send('You sent ' + req.params.id + ' as an id');
});

app.listen(7000);

Две строки, начинающиеся с app.get ()— это обработчики событий, которые включаются при поступлении запроса GET. Первый аргумент в этих вызовах представляет собой регулярное выражение, специфицирующее URL-адрес, который может передать клиент. Второй аргумент — это функция, которая в действительности будет обрабатывать запрос.

Аргументом регулярного выражения является механизм маршрутизации. Если тип запроса (GET, POST и т.д.) и ресурс (/, /root/123) соответствуют друг другу, производится вызов функции-обработчика. В первом вызове app.get() в качестве ресурса указываются символ /. Во втором вызове указываются символы /root, за которым следуют символы ID. Символ (:) перед ресурсом в URL-адресе, отображающем регулярное выражение, обозначает данный элемент как параметр, который может быть использован позднее.

Вызов функции обработчика осуществляется, когда тип запроса соответствует регулярному выражению. Эта функция имеет два аргумента — запрос (req) и ответ (res). Упомянутый выше параметр присоединяется к объекту request. Обратное сообщение веб-сервера пользователю передается в объект response.

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

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


Финальная порция знаний перед завершением статьи

Следует обратить внимание еще на две концепции/тенденции:

  • Неожиданная популярность баз данных типа "ключ/значение".
  • Другие асинхронные веб-парадигмы.

Почему базы данных типа "ключ/значение" неожиданно приобрели популярность?

JavaScript — это "лингва-франка" веб-приложений, поэтому любые разговоры на тему JavaScript находятся на расстоянии одного шага от обсуждения JSON (JavaScript Object Notation). JSON является самым распространенным способом обмена информацией между JavaScript и некоторыми другими языками. По существу, JSON — это хранилище ключей/значений; это обуславливает естественный интерес разработчиков JavaScript/Node.js к базам данных типа "ключ/значение". В конце концов, если JavaScript-разработчик сможет хранить данные в формате JSON, это существенно облегчит его работу.

Кроме того, о базах данных типа "ключ/значение" много говорится в контексте баз данных типа NoSQL. Теорема CAP (известная также как теорема Брюера), утверждает, что распределенная система способна обеспечить не более двух из трех следующих свойств: согласованность (consistency), доступность (availability) и устойчивость к разделению (partition tolerance): formal proof of CAP. Эта теорема является одной из движущих сил движения NoSQL; она создает основу для повышения доступности (как правило) ценой ухудшения некоторых характеристик традиционной реляционной базы данных. Несколько популярных баз данных типа "ключ/значение": Riak, Cassandra, CouchDB, MongoDB.

Асинхронные веб-парадигмы

Управляемые событиями асинхронные веб-инфраструктуры существуют уже довольно давно. Одна из наиболее популярных из недавно появившихся асинхронных веб-инфраструктур — Tornado; она написана на языке Python и используется внутри Facebook. Приведенный ниже пример демонстрирует, как выглядит упражнение "hello_world" в Tornado (файл hello_tornado.py в материалах для загрузки).

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

Инфраструктура Twisted.web, также написанная на Python, действует очень похожим образом.

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


Заключение

Node.js — это весьма привлекательный продукт для веб-разработчиков. Он позволяет группе разработчиков использовать JavaScript для написания кода на стороне клиента и на стороне сервера. При этом разработчики также могут задействовать мощные технологии, доступные в экосистеме JavaScript: в т.ч. JQuery, V8, JSON и управляемое событиями программирование. Кроме того, существуют экосистемы, развивающиеся "поверх" Node.js, такие как веб-инфраструктура Express.

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


Загрузка

ОписаниеИмяРазмер
Учебный программный код для данной статьиnodejs_src.zip6 КБ

Ресурсы

  • Оригинал статьи: Use Node.js as a full cloud environment development stack.
  • Концепции и технологии Node.js, упомянутые в этой статье:
    • Node.js: Назначение Node.js — упрощение построения масштабируемых сетевых программ.
    • Cloud Foundry: Сайт сообщества сторонников открытого исходного кода, на котором разработчики могут обмениваться информацией и вносить свой вклад в развитие этой открытой PaaS-среды.
    • Node.js guide: Неформальное вводное руководство по Node.js (автор — Феликс Гейзендорфер, Felix Geisendorfer). (Раздел сайта stackoverflow.com содержит ответы на вопросы новичков в области Node.js.)
    • High performance in Haskell: Грегори Коллинз (Gregory Collins), инженер по надежности сайта Google, использует Node.js для демонстрации управляемой событиями модели.
    • Tornado: Масштабируемый, относительно быстрый и неблокируемый веб-сервер — версия с открытым исходным кодом.
    • Cloud9 IDE: Современная IDE-среда, который исполняется в браузере клиента, а "живет" в облаке, что позволяет разработчику запускать, отлаживать и развертывать приложения Node.js из любого места.
    • Bulletproof Node.js coding: Стелла Лоренцо (Stella Laurenzo) объясняет написание кода в среде Node.js: "Чрезвычайно мощные возможности, весьма простые в применении".
    • Node Nerd: Ссылки и руководства по Node.js (автор — Кевин Горски, Kevin Gorski).
    • npm 1.0: Опробуйте инструмент Node Package Manager.
    • Riak: Созданная под влиянием Dynamo база данных, которая легко и прогнозируемым образом масштабируется и упрощает разработку посредством предоставления пользователям возможностей для быстрого написания, тестирования и развертывания приложений.
    • nginx: Высокопроизводительный HTTP-сервер/реверсивный прокси-сервер с открытым исходным кодом, с помощью которого осуществляется хостинг почти 7,65% всех доменов мира. Отличительные особенности: высокая производительность, стабильность, богатый набор функций, простота конфигурирования, низкое потребление ресурсов. Сервер nginx решает проблему C10K (одновременная поддержка 10000 клиентов).
    • Express: Инфраструктура для разработки веб-приложений, специально созданная для платформы Node.js и устраняющая некоторые из ее недостатков.
  • Статья на сайте developerWorks под названием Вторая волна разработки Java-приложений: JavaScript для разработчиков Java разъясняет, почему JavaScript является столь важным инструментом для современного Java-разработчика.
  • Другие материалы от Ноя Гифта (Noah Gift) на сайте developerWorks:
  • Райан Макгири (Ryan McGeary) обсуждает, как язык CoffeeScript, построенный поверх node.js, делает функциональное программирование чище [Слушать | Читать].

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Облачные вычисления
ArticleID=841930
ArticleTitle=Применение Node.js в качестве полной среды разработки для облачных решений
publish-date=10222012