NodeJS Эффективное программирование Юра Богданов технический директор и соучредитель Eventr

Содержание

Слайд 2

NodeJS Цель проекта: «Предоставить естественную неблокирующую, событийно-ориентированную инфраструктуру для написания программ

NodeJS

Цель проекта:

«Предоставить естественную неблокирующую, событийно-ориентированную инфраструктуру для написания программ с высокой

конкурентностью»
(с) Ryan Dahl

«to provide a purely evented, non-blocking infrastructure to script highly concurrent programs»

Слайд 3

NodeJS NodeJS – серверная JavaScript платформа Использует Google V8 (Chromium: Google

NodeJS

NodeJS – серверная JavaScript платформа
Использует Google V8 (Chromium: Google Chrome, Chrome

OS, etc.)
Превращает V8 в мощную машину для серверных приложений
Сливается в гармонии с философией JavaScript
Молодой, но живой
Event loop - неблокирующий ввод/вывод
Все выполняется параллельно, кроме вашего кода
Слайд 4

Для чего подходит NodeJS Много I/O + большая конкурентность RIA —

Для чего подходит NodeJS

Много I/O + большая конкурентность
RIA — «богатые» приложения
API
Proxy
Realtime
Чаты
Онлайн

игры
Трансляции
Publish/Subscribe
Слайд 5

Event loop Это цикл (libev) Это один процесс, один поток Выполняет

Event loop

Это цикл (libev)
Это один процесс, один поток
Выполняет одну задачу на

один момент времени
Ожидает события параллельно (libeio, pooled threads)
В каждой итерации последовательно запускает функции-колбэки из трех разных очередей:
nextTick функции
Таймеры (setTimeout, setInterval)
Сигналы ввода/вывода (libeio)
Завершает работу, если все очереди пусты
Слайд 6

Время CPU – процессорное время Интерпретация кода Бизнес-логика приложения, алгоритмы Рендеринг

Время

CPU – процессорное время
Интерпретация кода
Бизнес-логика приложения, алгоритмы
Рендеринг шаблонов
I/O – время ввода/вывода
Запросы

в базу данных (network)
Чтение файлов
Чтение кэша

I/O

CPU

Слайд 7

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

70ms
$html = renderUser($user); // CPU - 30ms

Упрощенный пример сценария веб-приложения На самом деле, у нас много I/O и много логики

Слайд 8

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

70ms
$html = renderUser($user); // CPU - 30ms

CPU
30%

I/O
70%

Слайд 9

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

70ms
$html = renderUser($user); // CPU - 30ms

I/O

CPU

занято

монолит

А что если...

Слайд 10

callback I/O I/O CPU CPU занято свободно Blocking I/O Event Loop

callback

I/O

I/O

CPU

CPU

занято

свободно

Blocking I/O

Event Loop

монолит

А что если во время ожидания I/O заниматься друмиги

полезными делами?
Слайд 11

I/O Event loop Примерно так выглядит более реальный запрос: CPU+I/O

I/O

Event loop
Примерно так выглядит более реальный запрос:

CPU+I/O

Слайд 12

I/O CPU callback I/O CPU Event loop Примерно так выглядит более реальный запрос:

I/O

CPU

callback

I/O

CPU

Event loop
Примерно так выглядит более реальный запрос:

Слайд 13

Event loop Примерно так выглядит более реальный запрос: I/O CPU Свободно

Event loop
Примерно так выглядит более реальный запрос:

I/O

CPU

Свободно для других задач

callback

I/O

CPU

Слайд 14

Event Loop mysql.query(‘SELECT count(*) FROM users’, function(err, count) { console.log(‘There are

Event Loop

mysql.query(‘SELECT count(*) FROM users’, function(err, count) {
console.log(‘There are %d

users in db’, count);
})
console.log(‘Hello, ’);

Отправка запроса в БД

Слайд 15

Event Loop mysql.query(‘SELECT count(*) FROM users’, function(err, count) { console.log(‘There are

Event Loop

mysql.query(‘SELECT count(*) FROM users’, function(err, count) {
console.log(‘There are %d

users in db’, count);
})
console.log(‘Hello, ’);

Hello,

Ожидание ответа БД...

Слайд 16

Event Loop mysql.query(‘SELECT count(*) FROM users’, function(err, count) { console.log(‘There are

Event Loop

mysql.query(‘SELECT count(*) FROM users’, function(err, count) {
console.log(‘There are %d

users in db’, count);
})
console.log(‘Hello, ’);

Hello,
There are 10231512 users in db

Пришел ответ из БД

Слайд 17

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

70ms
$html = renderUser($user); // CPU - 30ms

db.query('SELECT * FROM users WHERE id=1’, function(err, user) { // I/O 70ms
var html = renderUser(user); // CPU – 30ms
})

Слайд 18

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

$user = $db->query('SELECT * FROM users WHERE id=1'); // I/O -

70ms
$html = renderUser($user); // CPU - 30ms

db.query('SELECT * FROM users WHERE id=1’, function(err, user) { // I/O 70ms
var html = renderUser(user); // CPU – 30ms
})

CPU
30%

I/O
70%

Слайд 19

Первый запрос Blocking I/O, 1 процесс

Первый запрос

Blocking I/O, 1 процесс

Слайд 20

Второй запрос, после 10ms ожидает выполнения первого Blocking I/O, 1 процесс

Второй запрос, после 10ms ожидает выполнения первого

Blocking I/O, 1 процесс

Слайд 21

Третий запрос, после 50ms ожидает выполнения первого и второго Blocking I/O, 1 процесс

Третий запрос, после 50ms ожидает выполнения первого и второго

Blocking I/O, 1 процесс

Слайд 22

Blocking I/O, 1 процесс Event loop, 1 процесс

Blocking I/O, 1 процесс

Event loop, 1 процесс

Слайд 23

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 0ms Пришел

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 0ms
Пришел первый запрос,
Запрашиваем I/O,

освобождаемся,
Ждем других запросов
Слайд 24

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 10ms Пришел

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 10ms
Пришел второй запрос,
Запрашиваем I/O,

освобождаемся,
Ждем других запросов
Слайд 25

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 50ms Пришел

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 50ms
Пришел третий запрос,
Запрашиваем I/O,

освобождаемся,
Ждем других запросов
Слайд 26

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 70ms Пришел

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 70ms
Пришел ответ на I/O

первого запроса, запускаем callback1
Слайд 27

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 80ms Пришел

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 80ms
Пришел ответ на I/O

второго запроса, callback2 ожидает своей очереди
Слайд 28

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 100ms сallback1 завершился, запускаем callback2

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 100ms
сallback1 завершился, запускаем callback2

Слайд 29

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 120ms Пришел

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 120ms
Пришел ответ на I/O

третьего запроса, callback3 ожидает своей очереди
Слайд 30

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 130ms сallback2 завершился, запускаем callback3

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 130ms
сallback2 завершился, запускаем callback3

Слайд 31

Blocking I/O, 1 процесс Event loop, 1 процесс Время: 160ms callback3 завершился Ждем других запросов

Blocking I/O, 1 процесс

Event loop, 1 процесс

Время: 160ms
callback3 завершился
Ждем других запросов

Слайд 32

Время CPU vs I/O RIA трэнд

Время CPU vs I/O RIA трэнд

Слайд 33

Приложение my_app.js library / my_module.js node_modules / express sync narrow

Приложение

my_app.js
library /
my_module.js
node_modules /
express
sync
narrow

Слайд 34

Приложение my_app.js library / my_module.js node_modules / express sync narrow require.paths.unshift(‘./library’)

Приложение

my_app.js
library /
my_module.js
node_modules /
express
sync
narrow

require.paths.unshift(‘./library’)
var MyModule = require(‘my_module’)
var Sync = require(‘sync’)

Слайд 35

Приложение my_app.js library / my_module.js node_modules / express sync narrow var

Приложение

my_app.js
library /
my_module.js
node_modules /
express
sync
narrow

var MyModule = function() {
// …
}
module.exports = MyModule

Слайд 36

Приложение my_app.js library / my_module.js node_modules / express sync narrow require.paths.unshift(‘./library’)

Приложение

my_app.js
library /
my_module.js
node_modules /
express
sync
narrow

require.paths.unshift(‘./library’)
var MyModule = require(‘my_module’)
var Sync = require(‘sync’)
console.log(‘my module: ‘,

MyModule);
+
Слайд 37

Приложение my_app.js library / my_module.js node_modules / express sync narrow $

Приложение

my_app.js
library /
my_module.js
node_modules /
express
sync
narrow

$ node my_app.js
my module: function (){}

Слайд 38

Приложение my_app.js library / my_module.js node_modules / express sync narrow mongoose $ npm install mongoose

Приложение

my_app.js
library /
my_module.js
node_modules /
express
sync
narrow
mongoose

$ npm install mongoose

Слайд 39

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Подключаем HTTP модуль

Слайд 40

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Создаем HTTP сервер

Слайд 41

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

«вешаем» сервер на 3080 порт

Слайд 42

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Сервер создан,
выводим сообщение

Слайд 43

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Функция будет вызвана индивидуально для каждого запроса

Слайд 44

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Отправляем HTTP заголовок

Слайд 45

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Отправляем HTTP тело и закрываем сокет

Слайд 46

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js

Слайд 47

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js
Server running at http://127.0.0.1:3080/

Слайд 48

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js
Server running at http://127.0.0.1:3080/

$ curl http://127.0.0.1:3080/

Слайд 49

HTTP var http = require(‘http’); http.createServer(function(req, res){ res.writeHead(200, { ‘Content-Type’ :

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });

res.end(‘Hello, World\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js
Server running at http://127.0.0.1:3080/

$ curl http://127.0.0.1:3080/
Hello, World
$

Слайд 50

HTTP var http = require(‘http’); http.createServer(function(req, res){ setTimeout(function(){ res.end(‘World!\n’); }, 1000);

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
setTimeout(function(){
res.end(‘World!\n’);
}, 1000);
res.writeHead(200, {

‘Content-Type’ : ‘text/plain’ });
res.write(‘Hello,\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Hello сразу, World – через 1 сек
+
+
+
+

Слайд 51

HTTP var http = require(‘http’); http.createServer(function(req, res){ setTimeout(function(){ res.end(‘World!\n’); }, 1000);

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
setTimeout(function(){
res.end(‘World!\n’);
}, 1000);
res.writeHead(200, {

‘Content-Type’ : ‘text/plain’ });
res.write(‘Hello,\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js
Server running at http://127.0.0.1:3080/

$ curl http://127.0.0.1:3080/
Hello,

Слайд 52

HTTP var http = require(‘http’); http.createServer(function(req, res){ setTimeout(function(){ res.end(‘World!\n’); }, 1000);

HTTP

var http = require(‘http’);
http.createServer(function(req, res){
setTimeout(function(){
res.end(‘World!\n’);
}, 1000);
res.writeHead(200, {

‘Content-Type’ : ‘text/plain’ });
res.write(‘Hello,\n’);
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js
Server running at http://127.0.0.1:3080/

$ curl http://127.0.0.1:3080/
Hello,
World!
$

Через секунду

Слайд 53

HTTP var http = require(‘http’); var i = 0; http.createServer(function(req, res){

HTTP

var http = require(‘http’);
var i = 0;
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’

: ‘text/plain’ });
res.end(‘i = ’ + i + ‘\n’);
i++;
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

Итератор, общий для всех запросов – javascript замыкание (closure)

Слайд 54

HTTP var http = require(‘http’); var i = 0; http.createServer(function(req, res){

HTTP

var http = require(‘http’);
var i = 0;
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’

: ‘text/plain’ });
res.end(‘i = ’ + i + ‘\n’);
i++;
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js
Server running at http://127.0.0.1:3080/

$ curl http://127.0.0.1:3080/
i = 0
$

Слайд 55

HTTP var http = require(‘http’); var i = 0; http.createServer(function(req, res){

HTTP

var http = require(‘http’);
var i = 0;
http.createServer(function(req, res){
res.writeHead(200, { ‘Content-Type’

: ‘text/plain’ });
res.end(‘i = ’ + i + ‘\n’);
i++;
}).listen(3080)
console.log(‘Server running at http://127.0.0.1:3080/’);

$ node my_app.js
Server running at http://127.0.0.1:3080/

$ curl http://127.0.0.1:3080/
i = 0
$ curl http://127.0.0.1:3080/
i = 1
$

Слайд 56

Callback-driven парадигма Ломает мозг Синтаксический шум Сложно выполнить ряд действий в

Callback-driven парадигма

Ломает мозг
Синтаксический шум
Сложно выполнить ряд действий в определенной последовательности
Необходимость вручную

«пробрасывать» ошибки
Бесконечная индентация – aka «спагетти код»
Слайд 57

Callback-driven парадигма function asyncFunction(arg1, arg2, argN, callback) { } Неблокирующая функция принимает callback последним аргументом

Callback-driven парадигма

function asyncFunction(arg1, arg2, argN, callback) {
}

Неблокирующая функция принимает callback последним

аргументом
Слайд 58

Callback-driven парадигма function asyncFunction(arg1, arg2, argN, callback) { } function callback(err,

Callback-driven парадигма

function asyncFunction(arg1, arg2, argN, callback) {
}

function callback(err, result1, result2, resultN)

{
}

Неблокирующая функция принимает callback последним аргументом

Callback принимает ошибку первым аргументом, остальные – результат

Слайд 59

Callback-driven парадигма function sum(a, b) { if (a > b) {

Callback-driven парадигма

function sum(a, b) {
if (a > b) {
throw new Error('a

cannot be greater than b');
}
return a + b;
}
Слайд 60

Callback-driven парадигма function sum(a, b) { if (a > b) {

Callback-driven парадигма

function sum(a, b) {
if (a > b) {
throw new Error('a

cannot be greater than b');
}
return a + b;
}

function asyncSum(a, b, callback) {
if (a > b) {
return callback(new Error('a cannot be greater than b'));
}
callback(null, a + b);
}
+
+
+

Слайд 61

Callback-driven парадигма function sum(a, b) { if (a > b) {

Callback-driven парадигма

function sum(a, b) {
if (a > b) {
throw new Error('a

cannot be greater than b');
}
return a + b;
}

function asyncSum(a, b, callback) {
if (a > b) {
return callback(new Error('a cannot be greater than b'));
}
callback(null, a + b);
}
+
+
+

throw!

Слайд 62

Callback-driven парадигма try { var result = sum(2, 3); console.log('result =

Callback-driven парадигма

try {
var result = sum(2, 3);
console.log('result = %d', result);
} catch

(err) {
console.error(err);
}
Слайд 63

Callback-driven парадигма try { var result = sum(2, 3); console.log('result =

Callback-driven парадигма

try {
var result = sum(2, 3);
console.log('result = %d', result);
} catch

(err) {
console.error(err);
}

asyncSum(2, 3, function(err, result){
if (err) return console.error(err);
console.log('result = %d', result);
})
+
+
+

Слайд 64

Callback-driven парадигма function getUser(id, callback) { }

Callback-driven парадигма

function getUser(id, callback) {
}

Слайд 65

Callback-driven парадигма function getUser(id, callback) { } getUser(1234, function(err, user) {

Callback-driven парадигма

function getUser(id, callback) {
}
getUser(1234, function(err, user) {
if (err) return

console.error(err);
console.log(‘user: ’, user);
})
Слайд 66

Callback-driven парадигма function getUser(id, callback) { readConfig(‘config.json’, function(err, config){ if (err)

Callback-driven парадигма

function getUser(id, callback) {
readConfig(‘config.json’, function(err, config){
if (err) return

callback(err);
})
}
+
+
+
Слайд 67

Callback-driven парадигма function getUser(id, callback) { readConfig(‘config.json’, function(err, config){ if (err)

Callback-driven парадигма

function getUser(id, callback) {
readConfig(‘config.json’, function(err, config){
if (err) return

callback(err);
dbConnect(config.host, function(err, db){
if (err) return callback(err);
})
})
}
+
+
+
Слайд 68

Callback-driven парадигма function getUser(id, callback) { readConfig(‘config.json’, function(err, config){ if (err)

Callback-driven парадигма

function getUser(id, callback) {
readConfig(‘config.json’, function(err, config){
if (err) return

callback(err);
dbConnect(config.host, function(err, db){
if (err) return callback(err);
db.getUser(id, function(err, user){
if (err) return callback(err);
callback(null, user);
})
})
})
}
+
+
+
+
Слайд 69

Callback-driven парадигма function getUser(id, callback) { readConfig(‘config.json’, function(err, config){ if (err)

Callback-driven парадигма

function getUser(id, callback) {
readConfig(‘config.json’, function(err, config){
if (err) return

callback(err);
afterReadConfig(id, config, callback);
})
}
function afterReadConfig(id, config, callback) {
dbConnect(config.host, function(err, db){
if (err) return callback(err);
db.getUser(id, function(err, user){

})
}
+
+
+
+
+
+
+
Слайд 70

Callback-driven парадигма function getUser(id, callback) { readConfig(‘config.json’, function(err, config){ if (err)

Callback-driven парадигма

function getUser(id, callback) {
readConfig(‘config.json’, function(err, config){
if (err) return

callback(err);
afterReadConfig(id, config, callback);
})
}
function afterReadConfig(id, config, callback) {
dbConnect(config.host, function(err, db){
if (err) return callback(err);
afterDbConnect(id, db, callback);
})
}
function afterDbConnect(id, db, callback) …
+
+
Слайд 71

Callback-driven парадигма Error: User not found at afterDbConnect (/path/to/script.js:24:14) at /path/to/script.js:20:9

Callback-driven парадигма

Error: User not found
at afterDbConnect (/path/to/script.js:24:14)
at /path/to/script.js:20:9
at

dbConnect (/path/to/script.js:7:5)
at afterReadConfig (/path/to/script.js:18:5)
at /path/to/script.js:13:9
at readConfig (/path/to/script.js:3:5)
at getUser (/path/to/script.js:11:5)
at Object. (/path/to/script.js:28:1)
at Module._compile (module.js:404:26)
at Object..js (module.js:410:10)
Слайд 72

Callback-driven парадигма Error: User not found at afterDbConnect (/path/to/script.js:24:14) at /path/to/script.js:20:9

Callback-driven парадигма

Error: User not found
at afterDbConnect (/path/to/script.js:24:14)
at /path/to/script.js:20:9
at

dbConnect (/path/to/script.js:7:5)
at afterReadConfig (/path/to/script.js:18:5)
at /path/to/script.js:13:9
at readConfig (/path/to/script.js:3:5)
at getUser (/path/to/script.js:11:5)
at Object. (/path/to/script.js:28:1)
at Module._compile (module.js:404:26)
at Object..js (module.js:410:10)
Слайд 73

Callback-driven парадигма function getUser(id, callback) { readConfig(‘config.json’, function(err, config){ if (err)

Callback-driven парадигма

function getUser(id, callback) {
readConfig(‘config.json’, function(err, config){
if (err) return

callback(err);
afterReadConfig(id, config, callback);
})
}
function afterReadConfig(id, config, callback) {
dbConnect(config.host, function(err, db){
if (err) return callback(err);
afterDbConnect(id, db, callback);
})
}
function afterDbConnect(id, db, callback) …

Синтаксический шум – плата за Evented I/O

Слайд 74

Callback-driven парадигма function getUser(id, callback) { readConfig(‘config.json’, function(err, config){ if (err)

Callback-driven парадигма

function getUser(id, callback) {
readConfig(‘config.json’, function(err, config){
if (err) return

callback(err);
afterReadConfig(id, config, callback);
})
}
function afterReadConfig(id, config, callback) {
dbConnect(config.host, function(err, db){
if (err) return callback(err);
afterDbConnect(id, db, callback);
})
}
function afterDbConnect(id, db, callback) …

Синтаксический шум – плата за Evented I/O

PROFIT

PROFIT

Слайд 75

node-sync Function.prototype.sync = function(context, arguments…) Использует сопрограммы (coroutines) с++ Основан на

node-sync

Function.prototype.sync = function(context, arguments…)

Использует сопрограммы (coroutines) с++
Основан на node-fibers
Позволяет писать синхронно

на nodejs

https://github.com/0ctave/node-sync

https://github.com/laverdet/node-fibers

Слайд 76

var Sync = require(‘sync’); function getUser(id, callback) { Sync(function(){ var config

var Sync = require(‘sync’);
function getUser(id, callback) {
Sync(function(){
var config =

readConfig.sync(null, ‘config.json’);
var db = dbConnect.sync(null, config.host);
var user = db.getUser.sync(db, id);
return user;
}, callback)
}

Запускаем новое «волокно» (Fiber)

node-sync

Слайд 77

var Sync = require(‘sync’); function getUser(id, callback) { Sync(function(){ var config

var Sync = require(‘sync’);
function getUser(id, callback) {
Sync(function(){
var config =

readConfig.sync(null, ‘config.json’);
var db = dbConnect.sync(null, config.host);
var user = db.getUser.sync(db, id);
return user;
}, callback)
}

«Волокно» вернет значение или ошибку в callback

node-sync

Слайд 78

var Sync = require(‘sync’); function getUser(id, callback) { Sync(function(){ var config

var Sync = require(‘sync’);
function getUser(id, callback) {
Sync(function(){
var config =

readConfig.sync(null, ‘config.json’);
var db = dbConnect.sync(null, config.host);
var user = db.getUser.sync(db, id);
return user;
}, callback)
}

Функция readConfig вызывается синхронно и возвращает значение

node-sync

Слайд 79

var Sync = require(‘sync’); function getUser(id) { var config = readConfig.sync(null,

var Sync = require(‘sync’);
function getUser(id) {
var config = readConfig.sync(null, ‘config.json’);

var db = dbConnect.sync(null, config.host);
var user = db.getUser.sync(db, id);
return user;
}.async()

То же самое, только проще
(коллбэка нет)

node-sync

Слайд 80

var Sync = require(‘sync’); function getUser(id) { var config = readConfig.sync(null,

var Sync = require(‘sync’);
function getUser(id) {
var config = readConfig.sync(null, ‘config.json’);

var db = dbConnect.sync(null, config.host);
var user = db.getUser.sync(db, id);
return user;
}.async()

node-sync
getUser(1234, function(err, user) {
if (err) return console.error(err);
console.log(‘user: ’, user);
})

Слайд 81

var Sync = require(‘sync’); function getUser(id) { var config = readConfig.sync(null,

var Sync = require(‘sync’);
function getUser(id) {
var config = readConfig.sync(null, ‘config.json’);

var db = dbConnect.sync(null, config.host);
throw new Error(‘something went wrong’);
return user;
}.async()

node-sync
getUser(1234, function(err, user) {
if (err) return console.error(err);
console.log(‘user: ’, user);
})

Слайд 82

var Sync = require(‘sync’); function getUser(id) { var config = readConfig.sync(null,

var Sync = require(‘sync’);
function getUser(id) {
var config = readConfig.sync(null, ‘config.json’);

var db = dbConnect.sync(null, config.host);
var user = db.getUser.future(db, id);
var friends = db.getUserFriends.future(db, id);
return { user : user.result, friends : friends.result };
}.async()

node-sync

getUser и getUserFriends
выполняются параллельно

Слайд 83

var Sync = require(‘sync’); function getUser(id) { var config = readConfig.sync(null,

var Sync = require(‘sync’);
function getUser(id) {
var config = readConfig.sync(null, ‘config.json’);

var db = dbConnect.sync(null, config.host);
var user = db.getUser.future(db, id);
db.getUserFriends(id, friends = new Sync.Future());
return { user : user.result, friends : friends.result };
}.async()

node-sync

другой способ получения «тикета» future

Слайд 84

Callback-driven парадигма $pages = $db->fetchRows(‘SELECT * FROM pages’); foreach ($pages as

Callback-driven парадигма

$pages = $db->fetchRows(‘SELECT * FROM pages’);
foreach ($pages as $page) {

$contents = fetchUrl($page->url);
}
Слайд 85

Callback-driven парадигма $pages = $db->fetchRows(‘SELECT * FROM pages’); foreach ($pages as

Callback-driven парадигма

$pages = $db->fetchRows(‘SELECT * FROM pages’);
foreach ($pages as $page) {

$contents = fetchUrl($page->url);
}

OK

Слайд 86

Callback-driven парадигма $pages = $db->fetchRows(‘SELECT * FROM pages’); foreach ($pages as

Callback-driven парадигма

$pages = $db->fetchRows(‘SELECT * FROM pages’);
foreach ($pages as $page) {

$contents = fetchUrl($page->url);
}

db.fetchRows(‘SELECT * FROM pages’, function(err, pages){
pages.forEach(function(page){
fetchUrl(page.url, function(err, contents) {
})
})
});

OK

Слайд 87

Callback-driven парадигма $pages = $db->fetchRows(‘SELECT * FROM pages’); foreach ($pages as

Callback-driven парадигма

$pages = $db->fetchRows(‘SELECT * FROM pages’);
foreach ($pages as $page) {

$contents = fetchUrl($page->url);
}

db.fetchRows(‘SELECT * FROM pages’, function(err, pages){
pages.forEach(function(page){
fetchUrl(page.url, function(err, contents) {
})
})
});

x 100,000

OK

Слайд 88

Callback-driven парадигма db.fetchRows(‘SELECT * FROM pages’, function(err, pages){ var narrow =

Callback-driven парадигма

db.fetchRows(‘SELECT * FROM pages’, function(err, pages){
var narrow = new

Narrow(10, function(page, callback){
fetchUrl(page.url, function(err, contents) {
})
})
narrow.pushAll(pages);
});
+
+
+

https://github.com/0ctave/node-narrow

Слайд 89

Callback-driven парадигма db.fetchRows(‘SELECT * FROM pages’, function(err, pages){ var narrow =

Callback-driven парадигма

db.fetchRows(‘SELECT * FROM pages’, function(err, pages){
var narrow = new

Narrow(10, function(page, callback){
fetchUrl(page.url, function(err, contents) {
})
})
narrow.pushAll(pages);
});

x10 - OK
+
+
+

x 100,000 - OK

https://github.com/0ctave/node-narrow

Слайд 90

node-sync - использует сопрограммы (coroutines) streamline.js – транслирует код node-async –

node-sync - использует сопрограммы (coroutines)
streamline.js – транслирует код
node-async – целый инструментарий

для асинхронного программирования
Ваша собственная flow-control библиотека :)

Callback-driven решения?

https://github.com/0ctave/node-sync

https://github.com/Sage/streamlinejs

https://github.com/caolan/async

https://github.com/joyent/node/wiki/modules#async-flow

http://www.scribd.com/doc/40366684/Nodejs-Controlling-Flow

Слайд 91

Масштабирование Nodejs – is just node (c) Ryan Dahl

Масштабирование

Nodejs – is just node
(c) Ryan Dahl

Слайд 92

Масштабирование Nodejs – is just node (c) Ryan Dahl 1 ядро CPU = 1 nodejs процесс

Масштабирование

Nodejs – is just node
(c) Ryan Dahl

1 ядро CPU

= 1 nodejs процесс
Слайд 93

Масштабирование node-cluster Расширяемый Поддержка POSIX сигналов «Горячая» перезагрузка (zero-downtime) «Аккуратное» завершение

Масштабирование

node-cluster
Расширяемый
Поддержка POSIX сигналов
«Горячая» перезагрузка (zero-downtime)
«Аккуратное» завершение (graceful shutdown)
Автоматом перезапускает мертвые процессы
Не

оставляет «зомби»
Автоматом определяет соличество ядер CPU
Поддержка REPL
Статистика
PID файлы
Логи

https://github.com/LearnBoost/cluster

Слайд 94

HTTP Cluster var http = require(‘http’), cluster = require(‘cluster’); var server

HTTP Cluster

var http = require(‘http’), cluster = require(‘cluster’);
var server = http.createServer(function(req,

res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });
res.end(‘Hello from ‘ + process.pid + ‘\n’);
});
cluster(server).listen(3080);
console.log(‘Server at http://127.0.0.1:3080/ (pid: %d)’, process.pid);

Подключаем модуль cluster

https://github.com/LearnBoost/cluster

Слайд 95

HTTP Cluster var http = require(‘http’), cluster = require(‘cluster’); var server

HTTP Cluster

var http = require(‘http’), cluster = require(‘cluster’);
var server = http.createServer(function(req,

res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });
res.end(‘Hello from ‘ + process.pid + ‘\n’);
});
cluster(server).listen(3080);
console.log(‘Server at http://127.0.0.1:3080/ (pid: %d)’, process.pid);

Оборачиваем http сервер в кластер
и «вешаем» на 3080 порт

https://github.com/LearnBoost/cluster

Слайд 96

HTTP Cluster var http = require(‘http’), cluster = require(‘cluster’); var server

HTTP Cluster

var http = require(‘http’), cluster = require(‘cluster’);
var server = http.createServer(function(req,

res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });
res.end(‘Hello from ‘ + process.pid + ‘\n’);
});
cluster(server).listen(3080);
console.log(‘Server at http://127.0.0.1:3080/ (pid: %d)’, process.pid);

Дополнительно выводим PID

https://github.com/LearnBoost/cluster

Слайд 97

HTTP Cluster var http = require(‘http’), cluster = require(‘cluster’); var server

HTTP Cluster

var http = require(‘http’), cluster = require(‘cluster’);
var server = http.createServer(function(req,

res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });
res.end(‘Hello from ‘ + process.pid + ‘\n’);
});
cluster(server).listen(3080);
console.log(‘Server at http://127.0.0.1:3080/ (pid: %d)’, process.pid);

https://github.com/LearnBoost/cluster

$ node my_app.js
Server at http://127.0.0.1:3080/ (pid: 9254)
Server at http://127.0.0.1:3080/ (pid: 9255)
Server at http://127.0.0.1:3080/ (pid: 9256)

Слайд 98

HTTP Cluster var http = require(‘http’), cluster = require(‘cluster’); var server

HTTP Cluster

var http = require(‘http’), cluster = require(‘cluster’);
var server = http.createServer(function(req,

res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });
res.end(‘Hello from ‘ + process.pid + ‘\n’);
});
cluster(server).listen(3080);
console.log(‘Server at http://127.0.0.1:3080/ (pid: %d)’, process.pid);

https://github.com/LearnBoost/cluster

$ node my_app.js
Server at http://127.0.0.1:3080/ (pid: 9254)
Server at http://127.0.0.1:3080/ (pid: 9255)
Server at http://127.0.0.1:3080/ (pid: 9256)

$ curl http://127.0.0.1:3080/
Hello from 9255
$

Слайд 99

HTTP Cluster var http = require(‘http’), cluster = require(‘cluster’); var server

HTTP Cluster

var http = require(‘http’), cluster = require(‘cluster’);
var server = http.createServer(function(req,

res){
res.writeHead(200, { ‘Content-Type’ : ‘text/plain’ });
res.end(‘Hello from ‘ + process.pid + ‘\n’);
});
cluster(server).listen(3080);
console.log(‘Server at http://127.0.0.1:3080/ (pid: %d)’, process.pid);

https://github.com/LearnBoost/cluster

$ node my_app.js
Server at http://127.0.0.1:3080/ (pid: 9254)
Server at http://127.0.0.1:3080/ (pid: 9255)
Server at http://127.0.0.1:3080/ (pid: 9256)

$ curl http://127.0.0.1:3080/
Hello from 9255
$ curl http://127.0.0.1:3080/
Hello from 9256
$

Слайд 100

NodeJS + Много I/O Много Запросов + Event Loop = PROFIT

NodeJS

+

Много I/O

Много
Запросов

+

Event Loop

= PROFIT