Node.js введение в технологию, КПИ #ITmeetingKPI

70
Введение в технологию

description

Семинар по Node.js в КПИ 20 октября 2014. Докладчики: Тимур Шемсединов, Никита Савченко, Максим Петренко. Краткое содержание: * Что такое Node.js и как работает JavaScript в V8 * Профессионалы расскажут, почему они выбрали Node.js * Вы узнаете его сильные и слабые стороны и где его лучше применять * Будет полный обзор особеностей и внутреннего строения Node.js * Примеры внедрения и Highload-проекты * Вопросы развертывания, хостинг, тестирования, и отладки * Где и что учить, что читать, как осваивать

Transcript of Node.js введение в технологию, КПИ #ITmeetingKPI

Page 1: Node.js введение в технологию, КПИ  #ITmeetingKPI

Введение в технологию

Page 2: Node.js введение в технологию, КПИ  #ITmeetingKPI

От JavaScript никуда не деться

«Всё, что может быть написано на JavaScript, должно быть написано на нём»

«Any application that can be written in JavaScript, will eventually be written in JavaScript»

Jeff Atwood основатель Stack Overflow

Page 3: Node.js введение в технологию, КПИ  #ITmeetingKPI

Основные идеи Node.js

1. Один язык, один формат данных,одна парадигма, одна архитектура

СУБДМобильныеприложения Сервер

Браузер

Оконныеприложения

HTTP / JSONJSON

BSON

JavaScript

JavaScript

JavaScript

Page 4: Node.js введение в технологию, КПИ  #ITmeetingKPI

Основные идеи Node.js

2. Долгая жизнь приложений в оперативной памяти на стороне сервера

• Состояние памяти сохраняется между запросами• Структуры данных (объекты, модель)• Соединения с БД и открытые файлы• Конфигурация и инициализация

• Веб сервер внутри приложения, а не наоборот• Меньше IPC

Page 5: Node.js введение в технологию, КПИ  #ITmeetingKPI

Основные идеи Node.js

3. Без ввода/вывода еще быстрее, чем с асинхронным вводом/выводом

• Упреждающее чтение• Отложенная запись• Отдача всего из памяти

Page 6: Node.js введение в технологию, КПИ  #ITmeetingKPI

Преимущества JavaScript:

1. Имеет очень низкий порог входа;2. Высочайшая гибкость;3. Высокая скорость разработки;4. Практически идеальный уровень

абстракции;…

Page 7: Node.js введение в технологию, КПИ  #ITmeetingKPI

Недостатки JavaScript:

1. Имеет очень низкий порог входа;2. Высочайшая гибкость;3. Высокая скорость разработки;4. Практически идеальный уровень

абстракции;…

Page 8: Node.js введение в технологию, КПИ  #ITmeetingKPI

Особенности JavaScript

Наличие огромного количества особенностей.Как в синтаксических конструкциях, так и в логике программ.

“12” + 5 === “125”Но…“12” - 5 === 7

NaN !== NaNnull === null

[] + [] === “”[] + {} === “[object Object]”{} + [] === 0isNaN({} + {}) === true

Page 9: Node.js введение в технологию, КПИ  #ITmeetingKPI

Введение в NodeJS

Page 10: Node.js введение в технологию, КПИ  #ITmeetingKPI

Что такое NodeJS

• Open Source JavaScript-движок V8 от Google;• Обёртка и библиотеки на JavaScript;• libuv;• Оболочка, написанная на C++.

Page 11: Node.js введение в технологию, КПИ  #ITmeetingKPI

Google V8

• Разработка Google, исходный код которой был открыт в 2008 году;

• Самый оптимальный JavaScript-движок на сегодняшний день;

• Основа браузеров Google Chrome и Chromium.

Page 12: Node.js введение в технологию, КПИ  #ITmeetingKPI

Преимущества V8

• Базовый и оптимизирующий компилятор;• Компиляция JavaScript-кода непосредственно в

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

ускоряющих доступ к свойствам и вызовы функций.

Page 13: Node.js введение в технологию, КПИ  #ITmeetingKPI

Начало работы с NodeJS

1. Установка дистрибутива с nodejs.org;2. Создание *.js файлов;3. Запуск программ: $ node *.js;

Page 14: Node.js введение в технологию, КПИ  #ITmeetingKPI

Элементарный пример

console.log(“Hello, world!”);

hello.js:

$ node helloHello, world!

Page 15: Node.js введение в технологию, КПИ  #ITmeetingKPI

API, менеджер пакетов и package.json

$ npm init…$ npm install colors --save

/ /node_modules /colors ... main.js package.json

nethttpsbuffercrypto

path…

var ht

tp = r

equire

(“http

”);

Page 16: Node.js введение в технологию, КПИ  #ITmeetingKPI

package.json

{ "name": "Test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1“ }, "author": "", "license": "ISC"}

Page 17: Node.js введение в технологию, КПИ  #ITmeetingKPI

Система контроля версий

• Git;• GitHub;• Структура проектов;• Репозитории npm;• Командная работа.

Page 18: Node.js введение в технологию, КПИ  #ITmeetingKPI

Специфика NodeJS

Page 19: Node.js введение в технологию, КПИ  #ITmeetingKPI

Однопоточность и многопоточность

• Что дает однопоточность?• Действительно ли 1 процесс = 1 поток?• Как нагрузить все ядра?

• cluster = require('cluster') • npm install webworker-threads• самостоятельно делаем IPC

Page 20: Node.js введение в технологию, КПИ  #ITmeetingKPI

Межпроцессовое взаимодействие

• Родное IPC, cluster• Разделяемая память: Memcache?• Шина сообщений (MQ): ZeroMQ, RabbitMQ...• TCP, UDP, HTTP

Page 21: Node.js введение в технологию, КПИ  #ITmeetingKPI

Асинхронный ввод/вывод

• Реактор

• Очередь

• СМО

fs.readFile(‘a.txt’,onRead

);http.get(

‘http://...’,onGet

);console.log(‘done?’);

Page 22: Node.js введение в технологию, КПИ  #ITmeetingKPI

Производительность

• Производительность кода• Производительность сервера

Факторы производительности Node.js• Асинхронность• Состояние в памяти• Минимизация IPC

Page 23: Node.js введение в технологию, КПИ  #ITmeetingKPI

Память

• GC (RAM 32bit: 512Mb, 64bit:1.7Gb )• node --max-old-space-size=3000• delete obj.field; delete arr[5]; delete name;• node --nouse-idle-notification

--expose-gc application.js• Вызываем gc();

Page 24: Node.js введение в технологию, КПИ  #ITmeetingKPI

Профилирование

• npm install -g node-inspectornode --debug application.js

• npm install heapdump

var hd = require('heapdump'); hd.writeSnapshot(

'dump'+Date.now()+'.heapsnapshot');

Page 25: Node.js введение в технологию, КПИ  #ITmeetingKPI

Отладка

• Через node-inspector• Выводом в консоль console.log(); или в лог • Если ошибку не видно

• node --stack-trace-limit=1000 application.js• Может понадобиться фильтр

Page 26: Node.js введение в технологию, КПИ  #ITmeetingKPI

Ошибки

• Общепринято в Node: function(err, arguments...)• Создаем new Error('Eror message')• Секция try {...} catch(e) {...} finally {...}• Событие .on('error', fn) если EventEmitter• Домены domain.create(); .run(fn); .on('error', fn);• Перехватprocess.on('uncaughtException', function(err) { console.log('uncaught Exception: '+err.stack);});

Page 27: Node.js введение в технологию, КПИ  #ITmeetingKPI

Обновление кода и перезапуск

• Наблюдение за процессом forevernpm install forever -gforever start --spinSleepTime 10000 application.js

• Наблюдение за файламиhttp://livereload.com/http://habrahabr.ru/post/168091/

Page 28: Node.js введение в технологию, КПИ  #ITmeetingKPI

Callback’и и вложенность

Пример запроса:

request('http://www.google.com/api',function (error, response, body) { var googleData = JSON.parse(body); ...});

Page 29: Node.js введение в технологию, КПИ  #ITmeetingKPI

Callback’и и вложенность

request('http://www.google.com/api',function (gError, gResponse, gBody) {request('http://www.yandex.com/api',function (yError, yResponse, yBody) {request('http://www.twitter.com/api',function (tError, tResponse, tBody)

{...}

...

Page 30: Node.js введение в технологию, КПИ  #ITmeetingKPI

Callback’и: Библиотека async

async.parallel([ function(){ ... }, function(){ ... }], callback);

Другие методы: series, parallel, whilst, until, waterfall, compose, queue, cargo, retry, each, map, reduce, detect, every ...

Page 31: Node.js введение в технологию, КПИ  #ITmeetingKPI

Callback’и: Библиотека async

async.parallel([ function(callback){ request('http://google.com/api', function(error, response, body) { callback(error, body); }); }, function(callback){ request('http://yandex.com/api', function(error, response, body) { callback(error, body); }); }, function(callback){ request('http://twitter.com/api', function(error, response, body) { callback(error, body); }); },], function(err, results) {

...});

Page 32: Node.js введение в технологию, КПИ  #ITmeetingKPI

Callback’и: Библиотека async

async.parallel([ function(callback){ googleApiService(callback); }, function(callback){ yandexApiService(callback); }, function(callback){ twitterApiService(callback); },], function(err, results) {

...});

Page 33: Node.js введение в технологию, КПИ  #ITmeetingKPI

Callback’и: Promise (Q)

https://github.com/kriskowal/q

Promise.then(function(success) {...

}, function(error) {...

});

Page 34: Node.js введение в технологию, КПИ  #ITmeetingKPI

Callback’и: Promise (Q)

Q.fcall(googleApiService) .then(yandexApiService) .then(twitterApiService);

Q.all([ googleApiService(), yandexApiService(), twitterApiService() ]) .then();

Page 35: Node.js введение в технологию, КПИ  #ITmeetingKPI

Express

$ npm install express --save

Простой, нетребовательный и минималистический middleware-фреймворк для разработки WEB-приложений на NodeJS;

Page 36: Node.js введение в технологию, КПИ  #ITmeetingKPI

Простое приложение на express

var express = require(“express”), app = express();

app.get(“/”, function (request, response) { response.send(“Hello, stranger!”);});

app.listen(80);

Page 37: Node.js введение в технологию, КПИ  #ITmeetingKPI

Приложение на expressvar express = require(“express”), app = express(), storage = { “home”: { head: “Sweet home” }, “about”: { head: “” } };

app.use(express.static(__dirname + “/public”));

app.get(“/:page?”, function (request, response) {

var page = request.params.page, data = storage[page || “home”];

if (!data) return response.redirect(“/”); response.render(“home”, data);

});

app.listen(80);

Page 38: Node.js введение в технологию, КПИ  #ITmeetingKPI

Элегантность expressvar express = require('express');var path = require('path');var favicon = require('serve-favicon');var logger = require('morgan');var cookieParser = require('cookie-parser');var bodyParser = require('body-parser');

var routes = require('./routes/index');var users = require('./routes/users');

var app = express();

// view engine setupapp.set('views', path.join(__dirname, 'views'));app.set('view engine', 'jade');

// uncomment after placing your favicon in /public//app.use(favicon(__dirname + '/public/favicon.ico'));app.use(logger('dev'));app.use(bodyParser.json());app.use(bodyParser.urlencoded());app.use(cookieParser());app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);app.use('/users', users);

/// catch 404 and forwarding to error handlerapp.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err);});

/// error handlers

// development error handler// will print stacktraceif (app.get('env') === 'development') { app.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: err }); });}

// production error handler// no stacktraces leaked to userapp.use(function(err, req, res, next) { res.status(err.status || 500); res.render('error', { message: err.message, error: {} });});

app.use('/', routes);app.use('/users', users);

module.exports = app;

Page 39: Node.js введение в технологию, КПИ  #ITmeetingKPI

Сборка проектов

• Для чего собирать проекты;• Система сборки grunt;• Gruntfile;• Пакеты npm grunt-contrib-*.

Page 40: Node.js введение в технологию, КПИ  #ITmeetingKPI

Пример Gruntfile.jsmodule.exports = function(grunt) {

grunt.initConfig({ uglify: { dist: { files: { “build/app.js”: [“source/app.js”] } } } });

grunt.loadNpmTasks(“grunt-contrib-uglify”);

grunt.registerTask(“default”, [“uglify”]);

}

Page 41: Node.js введение в технологию, КПИ  #ITmeetingKPI
Page 42: Node.js введение в технологию, КПИ  #ITmeetingKPI

Grunt устанавливается как NPM (Node Package Manager) модуль.

> npm install -g grunt

> grunt

> grunt serve

> grunt build

Page 43: Node.js введение в технологию, КПИ  #ITmeetingKPI

Gruntfile.js

module.exports = function (grunt) { require('load-grunt-tasks')(grunt); var yeoman = { app: 'app' }; try { yeomanConfig.app = require('./bower.json').appPath || yeoman.app; } catch () {e}

grunt.initConfig({ yeoman: yeomanConfig,

Page 44: Node.js введение в технологию, КПИ  #ITmeetingKPI

grunt.registerTask('default', [ 'jshint', 'test', 'build' ]);

Grunt register tasks - default

Page 45: Node.js введение в технологию, КПИ  #ITmeetingKPI

Grunt register tasks - test

grunt.registerTask('test', [ 'clean:server', 'less:modulesToTmp', 'copy:viewsModulesToTmp', 'copy:jsModulesToTmp', 'concat:modulesToTmp', 'autoprefixer', 'connect:test', 'karma' ]);

Page 46: Node.js введение в технологию, КПИ  #ITmeetingKPI

Grunt register tasks - build grunt.registerTask('build', [ 'clean:dist', 'less:modulesToTmp', 'copy:viewsModulesToTmp', 'copy:jsModulesToTmp', 'useminPrepare', 'autoprefixer', 'copy:dist', 'concat', 'ngmin', 'uglify', 'imagemin', 'htmlmin', 'usemin', 'rev'

Page 47: Node.js введение в технологию, КПИ  #ITmeetingKPI

Grunt register tasks - server

grunt.registerTask('server', function (target) { if (target === 'dist') { return grunt.task.run(['build', 'open', 'connect:dist:keepalive']); } grunt.task.run([ 'clean:server', 'less:modulesToTmp', 'copy:viewsModulesToTmp', 'copy:jsModulesToTmp', 'concat:modulesToTmp', 'autoprefixer', 'configureProxies', 'connect:livereload', 'open', 'watch'

Page 48: Node.js введение в технологию, КПИ  #ITmeetingKPI

clean: { server: '<%= yeoman.temp %>', dist: { files: [{ dot: true, src: [ '<%= yeoman.temp %>', '<%= yeoman.dist %>' ] }] } }

Grunt tasks - clean

Page 49: Node.js введение в технологию, КПИ  #ITmeetingKPI

less: { modulesToTmp: { src: '<%= yeoman.app %>/modules/**/*.less', dest: '<%= yeoman.temp %>/css/main.css' } }

Grunt tasks - less

Page 50: Node.js введение в технологию, КПИ  #ITmeetingKPI

copy: { viewsModulesToTmp: { files: [{ expand: true, cwd: '<%= yeoman.app %>/modules/', dest: '<%= yeoman.temp %>/views', src: ['**/*.html'], rename: function(dest, src) { return dest + '/' + src.replace(/views\//gi, ''); } }]

Grunt tasks - copy

Page 51: Node.js введение в технологию, КПИ  #ITmeetingKPI

concat: { modulesToTmp: { options: { banner: '"use strict";\n', process: function(src, filepath) { return '// Source: ' + filepath + '\n' + src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1'); } }, files: { '<%= yeoman.temp %>/js/controllers.min.js' : '<%= yeoman.app %>/modules/**/*.Controllers.js', '<%= yeoman.temp %>/js/directives.min.js' : '<%= yeoman.app %>/modules/**/*.Directives.js', '<%= yeoman.temp %>/js/filters.min.js' : '<%= yeoman.app %>/modules/**/*.Filters.js', '<%= yeoman.temp %>/js/services.min.js' : '<%= yeoman.app %>/modules/**/*.Services.js' }}}

Grunt tasks - concat

Page 52: Node.js введение в технологию, КПИ  #ITmeetingKPI

var LIVERELOAD_PORT = 35729;var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });var proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest;var mountFolder = function (connect, dir) { return connect.static(require('path').resolve(dir)); };

connect: { options: { port: 8800, hostname: 'localhost' }, proxies: [{ context: '/api', host: '127.0.0.1', port: 7777, https: false, changeOrigin: false, xforward: false }],

livereload: { options: { middleware: function (connect) { return [ lrSnippet, proxySnippet, mountFolder(connect, '.tmp'), mountFolder(connect, yeomanConfig.app) ]; }}}

Grunt tasks - conect

Page 53: Node.js введение в технологию, КПИ  #ITmeetingKPI

open: { server: { url: 'http://localhost:<%= connect.options.port %>' } }

Grunt tasks - open

Page 54: Node.js введение в технологию, КПИ  #ITmeetingKPI

watch: { livereload: { options: { livereload: LIVERELOAD_PORT}, files: [ '<%= yeoman.temp %>/**/*.html', '<%= yeoman.temp %>/css/**/*.css', '<%= yeoman.temp %>/js/**/*.js', '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}']}, js: { files: ['<%= yeoman.app %>/modules/**/*.js'], tasks: ['copy:jsModulesToTmp', 'concat:modulesToTmp'] }, css: { files: ['<%= yeoman.app %>/modules/**/*.less'], tasks: ['less:modulesToTmp']}

Grunt tasks - watch

Page 55: Node.js введение в технологию, КПИ  #ITmeetingKPI
Page 56: Node.js введение в технологию, КПИ  #ITmeetingKPI
Page 57: Node.js введение в технологию, КПИ  #ITmeetingKPI
Page 58: Node.js введение в технологию, КПИ  #ITmeetingKPI

Примеры внедренияи Highload-проекты

Page 59: Node.js введение в технологию, КПИ  #ITmeetingKPI

Интерактивное телевидение 7Sense

• 10млн. открытых соединений на 15 серверах• 30тыс. request/second на одном сервере

• Impress Application Server• Server-Sent Events• IPC, ZeroMQ

Page 60: Node.js введение в технологию, КПИ  #ITmeetingKPI

Масштабирование Impress App ServerLoad Balancer

С1 С3

С0

C2N2C1N2C1N1 C1N3 C2N1 C2N3 C3N1 C3N2

Impress Cloud Controller

Impress Application Server

IPC

HTTP

ØMQpub/sub + req/rep

С2

C3N3

Page 61: Node.js введение в технологию, КПИ  #ITmeetingKPI

Жизненный цикл проекта

• Юниттесты и интеграционные тесты• Continuous Integration (CI)

• Travis-CI• drone.io

• Развертывание среды• vagrant• docker

Page 62: Node.js введение в технологию, КПИ  #ITmeetingKPI

Сферы применения node.js

Часто применяется для:

• cервера веб-приложений и SPA• сервера мобильных приложений• системы сборки для фронт-энда• чаты, меседжинг• игровые сервера• как заплатка к ПО на других технологиях• парсеры, кравлеры

Page 63: Node.js введение в технологию, КПИ  #ITmeetingKPI

Сферы применения node.js

Реже применяется для:

• оконные приложения: node-webkit• приложения баз данных и корпоративные• промышленная автоматизация и программирование

микроконтроллеров (arduino, espruino, tessel)• обработка и трансляция видео и звука

Page 64: Node.js введение в технологию, КПИ  #ITmeetingKPI

Сферы применения node.js

Редко применяется (но подходит) для:

• CMS, публикация контента• электронная коммерция и торговля

И плохо подходит:

• вычисления и моделирование• научные приложения

Page 65: Node.js введение в технологию, КПИ  #ITmeetingKPI

Тестирование. TDD.

Page 66: Node.js введение в технологию, КПИ  #ITmeetingKPI

karma: { unit: { configFile: 'karma.conf.js', singleRun: true } },

Page 67: Node.js введение в технологию, КПИ  #ITmeetingKPI

describe('Service: Auth', function () { // load the service's module beforeEach(module('devApp')); // instantiate service var Auth; beforeEach(inject(function (_Auth_) { Auth = _Auth_; })); it('should do something', function () { expect(!!Auth).toBe(true); });});

Page 68: Node.js введение в технологию, КПИ  #ITmeetingKPI

var should = require('should');var request = require('supertest');describe('GET /api/things', function() { it('should respond with JSON array', function(done) { request(app) .get('/api/things') .expect(200) .expect('Content-Type', /json/) .end(function(err, res) { if (err) return done(err); res.body.should.be.instanceof(Array); done(); });

Page 69: Node.js введение в технологию, КПИ  #ITmeetingKPI
Page 70: Node.js введение в технологию, КПИ  #ITmeetingKPI

Полезные материалы

• npm (www.npmjs.org);• nodejs.org/api;• learn.javascript.ru;• learn.javascript.ru/nodejs-screencast;• nodeschool.io;• habrahabr.ru/hub/nodejs;• nodeguide.ru;• …