PEDROFRANCESCHI
@pedroh96
pedro@pagar.me
github.com/pedrofranceschi
Node.js no
Assuntos
• O problema
Assuntos
• O problema
• Por que Node.js?
Assuntos
• O problema
• Por que Node.js?
• Problemas de Node.js
Assuntos
• O problema
• Por que Node.js?
• Problemas de Node.js
• Node.js“the right way”
Assuntos
• O problema
• Por que Node.js?
• Problemas de Node.js
• Node.js“the right way”
• Repensando a infraestrutura (+ Microservices)
Assuntos
• O problema
• Por que Node.js?
• Problemas de Node.js
• Node.js“the right way”
• Repensando a infraestrutura (+ Microservices)
• Deployment (+ Continuous Integration)
Assuntos
• O problema
• Por que Node.js?
• Problemas de Node.js
• Node.js“the right way”
• Repensando a infraestrutura (+ Microservices)
• Deployment (+ Continuous Integration)
• Conclusões…
Assuntos
O problema
“Montar um gateway de pagamentos
amigável para desenvolvedores e
empreendedores”
“A forma mais simples de receber
pagamentos online”
API RESTful
Dashboard
Angular.js
Ruby
Node.js
Python
Java
.NET
PHP
C#
Premissas da API
• Simplicidade: RESTful + JSON
Premissas da API
• Simplicidade: RESTful + JSON
• Ambiente de testes isolado e decente
Premissas da API
• Simplicidade: RESTful + JSON
• Ambiente de testes isolado e decente
• Segurança sem comprometer simplicidade (PCI)
Premissas da API
• Simplicidade: RESTful + JSON
• Ambiente de testes isolado e decente
• Segurança sem comprometer simplicidade (PCI)
• Uptime de 99,9%
Premissas da API
• Simplicidade: RESTful + JSON
• Ambiente de testes isolado e decente
• Segurança sem comprometer simplicidade (PCI)
• Uptime de 99,9%
• Escalabilidade
Premissas da API
Por que Node.js?
Por que Node.js?
Request de transação
API RESTful
Sistema antifraude
fraude
legítimaAdquirente
(Cielo, Rede, etc)
sucesso/erro
erro
Por que Node.js?
Request de transação
API RESTful
Sistema antifraude
fraude
legítimaAdquirente
(Cielo, Rede, etc)
10.000 ms
3.000
m
s
sucesso/erro
erro
Por que Node.js?
(no nosso caso)
Por que Node.js?
(no nosso caso)
• Requests externos demorados (>1.000ms)
Por que Node.js?
(no nosso caso)
• Requests externos demorados (>1.000ms)
• I/O intenso em banco de dados
Por que Node.js?
(no nosso caso)
• Requests externos demorados (>1.000ms)
• I/O intenso em banco de dados
• Totalmente assíncrono, single thread e event-based
Por que Node.js?
(no nosso caso)
• Requests externos demorados (>1.000ms)
• I/O intenso em banco de dados
• Totalmente assíncrono, single thread e event-based
• Alta carga na aplicação
Por que Node.js?
(no nosso caso)
• Requests externos demorados (>1.000ms)
• I/O intenso em banco de dados
• Totalmente assíncrono, single thread e event-based
• Alta carga na aplicação
• Pouco processamento (sem blocking de CPU)
Por que Node.js?
(no nosso caso)
“Mas é só usar threads em qualquer
linguagem!..”
Por que Node.js?
(no nosso caso)
“Mas é só usar threads em qualquer
linguagem!..”
Não.
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1.000 threads por segundo
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1.000 threads por segundo
… se cada request leva em média 10 segundos …
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1.000 threads por segundo
… se cada request leva em média 10 segundos …
Em 9 segundos, teremos 9.000 threads
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1.000 threads por segundo
… se cada request leva em média 10 segundos …
Em 9 segundos, teremos 9.000 threads
Isso escala? :P :P :P
Por que Node.js?
(no nosso caso)
Agora, em Node.js…
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1 thread
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1 thread
… se cada request leva em média 10 segundos …
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1 thread
… se cada request leva em média 10 segundos …
Em 9 segundos, teremos 1 thread
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1 thread
… se cada request leva em média 10 segundos …
Em 9 segundos, teremos 1 thread
Isso escala? Sim.
Por que Node.js?
(no nosso caso)
1.000 requests por segundo com conexão a uma API
externa + I/O no DB = 1 thread
… se cada request leva em média 10 segundos …
Em 9 segundos, teremos 1 thread
Isso escala? Sim.
#eventloopFTW
I/O síncrona
Aplicação
Sistema operacional
I/O síncrona
Aplicação
Sistema operacional
I/O
I/O síncrona
Aplicação
Sistema operacional
I/O
I/O síncrona
Aplicação
Sistema operacional
I/O
I/O bloqueante
(aplicação travada)
I/O síncrona
Aplicação
Sistema operacional
I/O
I/O bloqueante
(aplicação travada)
I/O síncrona
Aplicação
Sistema operacional
I/O
retorno
I/O bloqueante
(aplicação travada)
I/O síncrona
Aplicação
Sistema operacional
I/O
retorno
I/O bloqueante
(aplicação travada)
I/O
retorno
I/O bloqueante
(aplicação travada)
I/O síncrona
Aplicação
Sistema operacional
I/O
retorno
I/O bloqueante
(aplicação travada)
I/O
retorno
I/O bloqueante
(aplicação travada)
I/O
retorno
I/O bloqueante
(aplicação travada)
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
I/O bloqueante
(thread travada)
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
I/O bloqueante
(thread travada)
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
I/O bloqueante
(thread travada)
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
callback
I/O bloqueante
(thread travada)
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
callback
I/O bloqueante
(thread travada)
I/O
Thread
callback
I/O bloqueante
(thread travada)
I/O“assíncrona”com threads
Aplicação
Sistema operacional
I/O
Thread
callback
I/O bloqueante
(thread travada)
I/O
Thread
callback
I/O bloqueante
(thread travada)
I/O
Thread
callback
I/O bloqueante
(thread travada)
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
I/O
callback
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
I/O
callback
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
……………………………………
I/O
callback
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
……………………………………
I/O
callback
I/O
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
…………………………………………………………………………
I/O
callback
I/O
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
…………………………………………………………………………
I/O
callback
I/O
I/O
callback
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
operações assíncronas em nível de OS
…………………………………………………………………………
I/O
callback
I/O
I/O
callback
callback
Node.js escala em I/O bloqueante, não
em utilização de CPU
(threads são boas em processamento paralelo)
Problemas de Node.js
Código assíncrono (race conditions, callback hell,
testes assíncronos, etc)
db.query("SELECT a FROM users WHERE ...;", function (err, result1) {
db.query("SELECT b FROM users WHERE ...;", function (err, result2) {
db.query("SELECT c FROM users WHERE ...;", function (err, result3) {
db.query("SELECT d FROM users WHERE ...;", function (err, result4) {
db.query("SELECT e FROM users WHERE ...;", function (err, result5) {
console.log("Finished.");
});
});
});
});
});
Um ótimo exemplo do que não fazer: callback hell.
Problemas de Node.js
Problemas de Javascript: bizarrices e facilidade em não
seguir padrões e orientação a objetos.
> 0.1+0.2
0.30000000000000004
> typeof NaN
'number'
> NaN === NaN
false
Cortesia do wtfjs.com
Problemas de Node.js
Exceptions não tratadas matam o processo.
var name = “Pedro Franceschi";
console.log("Tamanho do primeiro nome: " + name.split(" ")[0].length);
console.log("Tamanho do segundo nome: " + name.split(" ")[1].length);
Problemas de Node.js
$ node test.js
Tamanho do primeiro nome: 5
Tamanho do segundo nome: 10
Exceptions não tratadas matam o processo.
var name = “Pedro";
console.log("Tamanho do primeiro nome: " + name.split(" ")[0].length);
console.log("Tamanho do segundo nome: " + name.split(" ")[1].length);
Problemas de Node.js
$ node test.js
Tamanho do primeiro nome: 5
/private/tmp/test.js:4
console.log("Tamanho do segundo nome: " + name.split(" ")[1].length);
^
TypeError: Cannot read property 'length' of undefined
at Object.<anonymous> (/private/tmp/test.js:4:61)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:901:3
Problemas de Node.js
Problemas de Node.js
• Single thread (escalar horizontalmente e
verticalmente com múltiplas instâncias)
Problemas de Node.js
• Single thread (escalar horizontalmente e
verticalmente com múltiplas instâncias)
• Leaks de memória difíceis de detectar (back-end em
C++, variáveis globalizadas, closures, etc)
Problemas de Node.js
• Single thread (escalar horizontalmente e
verticalmente com múltiplas instâncias)
• Leaks de memória difíceis de detectar (back-end em
C++, variáveis globalizadas, closures, etc)
• Programadores front-end mexendo em back-end
(“é tudo Javascript!!”)
Problemas de Node.js
• Single thread (escalar horizontalmente e
verticalmente com múltiplas instâncias)
• Leaks de memória difíceis de detectar (back-end em
C++, variáveis globalizadas, closures, etc)
• Programadores front-end mexendo em back-end
(“é tudo Javascript!!”)
• Existe a 6 anos, porém ainda é beta (v0.12.0) - e às
vezes você precisa usar unstable em produção
Node.js“the right way”
Qualidade de vida vs. tempo usando Node
Qualidade de vida vs. tempo usando Node
Node é muito
legal!!!
Qualidade de vida vs. tempo usando Node
Node é muito
legal!!! Ops… Meu código está
ficando uma zona…
Qualidade de vida vs. tempo usando Node
Node é muito
legal!!! Ops… Meu código está
ficando uma zona…
Queria ter feito em
Rails…
Qualidade de vida vs. tempo usando Node
Node é muito
legal!!! Ops… Meu código está
ficando uma zona…
Queria ter feito em
Rails…
MVC de verdade +
Promise + Bluebird <3
Modules
Node.js “the right way”
var PI = Math.PI;
exports.area = function (r) {
return PI * r * r;
};
exports.circumference = function (r) {
return 2 * PI * r;
};
circle.js
var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is ' + circle.area(4));
main.js
Node.js “the right way”
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
[1,2,3].indexOf(5).should.equal(-1);
})
})
describe(‘#indexOf() after one second', function(){
before(function(done){
setTimeout(function(){
done();
}, 1000);
});
it('should return -1 when the value is not present', function(){
[1,2,3].indexOf(5).should.equal(-1);
})
})
});
Testes (JavaScript quebra)
(https://github.com/visionmedia/mocha e https://github.com/visionmedia/should.js/)
Node.js “the right way”
Models (Sequelize/Mongoose)
var Transaction = Sequelize.define('Transaction', {
amount: Sequelize.INTEGER,
cost: Sequelize.FLOAT,
status: {
type: Sequelize.ENUM,
values: ['paid', 'refused', 'refunded'],
defaultValue: 'processing'
}
}, {
instanceMethods: {
calculateCost: function(status){
status = status || this.status;
if(status == 'paid') {
return 50 + (this.amount * 0.015);
}
},
},
classMethods: {
},
hooks: {
beforeUpdate: function(transaction) {
return transaction.calculateCost()
.then(function(cost) {
transaction.cost = cost;
});
}
},
});
Node.js “the right way”
Models (Sequelize/Mongoose)
var Transaction = Sequelize.define('Transaction', {
amount: Sequelize.INTEGER,
cost: Sequelize.FLOAT,
status: {
type: Sequelize.ENUM,
values: ['paid', 'refused', 'refunded'],
defaultValue: 'processing'
}
}, {
instanceMethods: {
calculateCost: function(status){
status = status || this.status;
if(status == 'paid') {
return 50 + (this.amount * 0.015);
}
},
},
classMethods: {
},
hooks: {
beforeUpdate: function(transaction) {
return transaction.calculateCost()
.then(function(cost) {
transaction.cost = cost;
});
}
},
});
Node.js “the right way”
Models (Sequelize/Mongoose)
var Transaction = Sequelize.define('Transaction', {
amount: Sequelize.INTEGER,
cost: Sequelize.FLOAT,
status: {
type: Sequelize.ENUM,
values: ['paid', 'refused', 'refunded'],
defaultValue: 'processing'
}
}, {
instanceMethods: {
calculateCost: function(status){
status = status || this.status;
if(status == 'paid') {
return 50 + (this.amount * 0.015);
}
},
},
classMethods: {
},
hooks: {
beforeUpdate: function(transaction) {
return transaction.calculateCost()
.then(function(cost) {
transaction.cost = cost;
});
}
},
});
Node.js “the right way”
Models (Sequelize/Mongoose)
var Transaction = Sequelize.define('Transaction', {
amount: Sequelize.INTEGER,
cost: Sequelize.FLOAT,
status: {
type: Sequelize.ENUM,
values: ['paid', 'refused', 'refunded'],
defaultValue: 'processing'
}
}, {
instanceMethods: {
calculateCost: function(status){
status = status || this.status;
if(status == 'paid') {
return 50 + (this.amount * 0.015);
}
},
},
classMethods: {
},
hooks: {
beforeUpdate: function(transaction) {
return transaction.calculateCost()
.then(function(cost) {
transaction.cost = cost;
});
}
},
});
Node.js “the right way”
Models (Sequelize/Mongoose)
var Transaction = Sequelize.define('Transaction', {
amount: Sequelize.INTEGER,
cost: Sequelize.FLOAT,
status: {
type: Sequelize.ENUM,
values: ['paid', 'refused', 'refunded'],
defaultValue: 'processing'
}
}, {
instanceMethods: {
calculateCost: function(status){
status = status || this.status;
if(status == 'paid') {
return 50 + (this.amount * 0.015);
}
},
},
classMethods: {
},
hooks: {
beforeUpdate: function(transaction) {
return transaction.calculateCost()
.then(function(cost) {
transaction.cost = cost;
});
}
},
});
Node.js “the right way”
Promises (Bluebird)
fs.readFile("file.json", function(err, val) {
if(err) {
console.error("unable to read file");
} else {
try {
val = JSON.parse(val);
console.log(val.success);
} catch(e) {
console.error("invalid json in file");
}
}
});
Node.js “the right way”
Promises (Bluebird)
fs.readFile("file.json", function(err, val) {
if(err) {
console.error("unable to read file");
} else {
try {
val = JSON.parse(val);
console.log(val.success);
} catch(e) {
console.error("invalid json in file");
}
}
});
Node.js “the right way”
Promises (Bluebird)
fs.readFileAsync("file.json").then(JSON.parse).then(function(val) {
console.log(val.success);
})
.catch(SyntaxError, function(e) {
console.error("invalid json in file");
})
.catch(function(e){
console.error("unable to read file")
});
Assim é bem melhor :)
Node.js “the right way”
Lodash
var lines = [
['name', 'type', 'cost'],
['iPhone', 'cellphone', '2000'],
['MacBook', 'computer', '10000'],
['iPad', 'tablet', '1500']
];
var joinedLines = [];
for(var i = 0; i < lines.length; i++) {
joinedLines.push(lines[i].join(','));
}
var csvContent = joinedLines.join('n');
name,type,cost
iPhone,cellphone,2000
MacBook,computer,10000
iPad,tablet,1500
Output
Node.js “the right way”
Lodash
var lines = [
['name', 'type', 'cost'],
['iPhone', 'cellphone', '2000'],
['MacBook', 'computer', '10000'],
['iPad', 'tablet', '1500']
];
var joinedLines = [];
for(var i = 0; i < lines.length; i++) {
joinedLines.push(lines[i].join(','));
}
var csvContent = joinedLines.join('n');
name,type,cost
iPhone,cellphone,2000
MacBook,computer,10000
iPad,tablet,1500
Output
Node.js “the right way”
Lodash
var lines = [
['name', 'type', 'cost'],
['iPhone', 'cellphone', '2000'],
['MacBook', 'computer', '10000'],
['iPad', 'tablet', '1500']
];
var joinedLines = [];
for(var i = 0; i < lines.length; i++) {
joinedLines.push(lines[i].join(','));
}
var csvContent = joinedLines.join('n');
name,type,cost
iPhone,cellphone,2000
MacBook,computer,10000
iPad,tablet,1500
Output
Node.js “the right way”
Assim é bem melhor :)
Lodash
var _ = require('lodash');
var lines = [
['name', 'type', 'cost'],
['iPhone', 'cellphone', '2000'],
['MacBook', 'computer', '10000'],
['iPad', 'tablet', '1500']
];
var csvContent = _.map(lines, function(line){
return line.join(',');
}).join('n');
name,type,cost
iPhone,cellphone,2000
MacBook,computer,10000
iPad,tablet,1500
Output
Node.js “the right way”
Node.js “the right way”
• Seguir e manter um code style (dica: Google
JavaScript Style Guide)
Node.js “the right way”
• Seguir e manter um code style (dica: Google
JavaScript Style Guide)
• Tratamento de erros consistente via Promise (evitar
que processos morram)
Node.js “the right way”
• Seguir e manter um code style (dica: Google
JavaScript Style Guide)
• Tratamento de erros consistente via Promise (evitar
que processos morram)
• Manter consistência entre as bibliotecas de Promise
(i.e. usar a mesma do wrapper do database)
Node.js “the right way”
• Seguir e manter um code style (dica: Google
JavaScript Style Guide)
• Tratamento de erros consistente via Promise (evitar
que processos morram)
• Manter consistência entre as bibliotecas de Promise
(i.e. usar a mesma do wrapper do database)
• Sem for(var i = 0; i < object.length; i++) - #lodashFTW
Node.js “the right way”
utils
Node.js “the right way”
utils
• Express.js: lightweight HTTP framework
Node.js “the right way”
utils
• Express.js: lightweight HTTP framework
• Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc.
Node.js “the right way”
utils
• Express.js: lightweight HTTP framework
• Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc.
• Mongoose: ORM de MongoDB
Node.js “the right way”
utils
• Express.js: lightweight HTTP framework
• Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc.
• Mongoose: ORM de MongoDB
• Commander: wrapper de command line
Node.js “the right way”
utils
• Express.js: lightweight HTTP framework
• Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc.
• Mongoose: ORM de MongoDB
• Commander: wrapper de command line
• Vim: melhor editor de texto :P
Repensando a infraestrutura
Infraestrutura do Pagar.me
Router
api.pagar.me
Servidor da API
(Node.js)
Ambiente de testes
(sandbox dos clientes)
Ambiente de produção
Servidor da API
(Node.js)
Infraestrutura do Pagar.me
Router
api.pagar.me
Servidor da API
(Node.js)
ElasticSearchElasticSearch
Ambiente de testes
(sandbox dos clientes)
Ambiente de produção
Servidor da API
(Node.js)
Infraestrutura do Pagar.me
Router
api.pagar.me
Servidor da API
(Node.js)
ElasticSearchElasticSearch
MySQL
(transações e dados relacionais)
MySQL
(transações e dados relacionais)
Ambiente de testes
(sandbox dos clientes)
Ambiente de produção
Servidor da API
(Node.js)
Infraestrutura do Pagar.me
Router
api.pagar.me
Servidor da API
(Node.js)
ElasticSearchElasticSearch
MySQL
(transações e dados relacionais)
MySQL
(transações e dados relacionais)
Ambiente de testes
(sandbox dos clientes)
Ambiente de produção
Servidor da API
(Node.js)
Redis
+
Redis
+
Infraestrutura do Pagar.me
Router
api.pagar.me
Servidor da API
(Node.js)
ElasticSearchElasticSearch
MySQL
(transações e dados relacionais)
MySQL
(transações e dados relacionais)
MongoDB
(dados de clientes e não relacionais)
Ambiente de testes
(sandbox dos clientes)
Ambiente de produção
Servidor da API
(Node.js)
Redis
+
Redis
+
Por que tantos bancos?
Por que tantos bancos?
• Separar dados de teste (sandbox dos clientes) dos dados de
produção
Por que tantos bancos?
• Separar dados de teste (sandbox dos clientes) dos dados de
produção
• MySQL: dados relacionais (transações, assinaturas, planos,
cartões, etc.)
Por que tantos bancos?
• Separar dados de teste (sandbox dos clientes) dos dados de
produção
• MySQL: dados relacionais (transações, assinaturas, planos,
cartões, etc.)
• MongoDB: dados não-relacionais (informações do cliente,
usuários de uma conta, etc.)
Por que tantos bancos?
• Separar dados de teste (sandbox dos clientes) dos dados de
produção
• MySQL: dados relacionais (transações, assinaturas, planos,
cartões, etc.)
• MongoDB: dados não-relacionais (informações do cliente,
usuários de uma conta, etc.)
• ElasticSearch: indexação/buscas ultra-rápidas (expondo uma
engine de buscas poderosa para os clientes)
Por que tantos bancos?
• Separar dados de teste (sandbox dos clientes) dos dados de
produção
• MySQL: dados relacionais (transações, assinaturas, planos,
cartões, etc.)
• MongoDB: dados não-relacionais (informações do cliente,
usuários de uma conta, etc.)
• ElasticSearch: indexação/buscas ultra-rápidas (expondo uma
engine de buscas poderosa para os clientes)
• Redis: fila para notificações entre serviços
Por que tantos bancos?
• Separar dados de teste (sandbox dos clientes) dos dados de
produção
• MySQL: dados relacionais (transações, assinaturas, planos,
cartões, etc.)
• MongoDB: dados não-relacionais (informações do cliente,
usuários de uma conta, etc.)
• ElasticSearch: indexação/buscas ultra-rápidas (expondo uma
engine de buscas poderosa para os clientes)
• Redis: fila para notificações entre serviços
• Não há porque se prender a uma tecnologia quando cada uma
delas resolve uma parte do seu problema
Infraestrutura do Pagar.me
Router
api.pagar.me
Servidor da API
(Node.js)
ElasticSearchElasticSearch
MySQL
(transações e dados relacionais)
MySQL
(transações e dados relacionais)
MongoDB
(dados de clientes e não relacionais)
Ambiente de testes
(sandbox dos clientes)
Ambiente de produção
Servidor da API
(Node.js)
Redis
+
Redis
+
Infraestrutura do Pagar.me
Router
api.pagar.me
Servidor da API
(Node.js)
ElasticSearchElasticSearch
MySQL
(transações e dados relacionais)
MySQL
(transações e dados relacionais)
MongoDB
(dados de clientes e não relacionais)
Ambiente de testes
(sandbox dos clientes)
Ambiente de produção
Servidor da API
(Node.js)
Redis
+
Redis
+
Microservices
Cliente
Cliente
node api.js -p 3000
node gateway.js -p 5000
Cliente
node api.js -p 3000
node gateway.js -p 5000
API RESTful (pagar.me/docs)
Cliente
node api.js -p 3000
node gateway.js -p 5000
API RESTful (pagar.me/docs)
API RESTful
(serviço interno)
Cliente
node api.js -p 3000
node gateway.js -p 5000
API RESTful (pagar.me/docs)
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P
API RESTful
(serviço interno)
Cliente
node api.js -p 3000
node gateway.js -p 5000
API RESTful (pagar.me/docs)
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P
~ 550 ms
API RESTful
(serviço interno)
Cliente
node api.js -p 3000
node gateway.js -p 5000
API RESTful (pagar.me/docs)~ 500ms
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P
~ 550 ms
API RESTful
(serviço interno)
Cliente
node api.js -p 3000
node gateway.js -p 5000
API RESTful (pagar.me/docs)~ 500ms
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms
~ 550 ms
API RESTful
(serviço interno)
Cliente
node api.js -p 3000
node gateway.js -p 5000
node hookshot.js
API RESTful (pagar.me/docs)~ 500ms
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms
~ 550 ms
API RESTful
(serviço interno)
Cliente
node api.js -p 3000
node gateway.js -p 5000
node hookshot.js
API RESTful (pagar.me/docs)~ 500ms
Redis (queue)
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms
~ 550 ms
API RESTful
(serviço interno)
Cliente
node api.js -p 3000
node gateway.js -p 5000
node hookshot.js
API RESTful (pagar.me/docs)~ 500ms
Redis (queue)
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms
~ 550 ms
API RESTful
(serviço interno)
Request HTTP para
o servidor do cliente
Cliente
node api.js -p 3000
node gateway.js -p 5000
node hookshot.js
API RESTful (pagar.me/docs)~ 500ms
Redis (queue)
XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms
~ 550 ms
API RESTful
(serviço interno)
~ 100 ms
Request HTTP para
o servidor do cliente
Deployment
Nível 1
$ node server.js
n00bz… Processo não roda em background
Nível 2
$ git pull && npm install && node server.js &
Opa… Agora tem Git, update das dependências pelo NPM e o
processo roda em background
Nível 3
$ git pull && npm install && nohup node server.js
Nohup roda o processo mesmo depois do logout do SSH
Nível 4
$ git pull && npm install && service node-server restart
Um serviço é responsável por rodar e reiniciar o processo e
salvar os logs do processo
Nível 5
$ service node-server restart
Agora o servidor de CI lida com o Git e as dependências
Servidor de Continuous Integration (CI)
+
pm2
(https://github.com/Unitech/pm2)
pm2
(https://github.com/Unitech/pm2)
• Roda e gerencia os processos do Node.js (mantém
processo rodando para sempre)
pm2
(https://github.com/Unitech/pm2)
• Roda e gerencia os processos do Node.js (mantém
processo rodando para sempre)
• Reload no código on-the-fly (zero downtime)
pm2
(https://github.com/Unitech/pm2)
• Roda e gerencia os processos do Node.js (mantém
processo rodando para sempre)
• Reload no código on-the-fly (zero downtime)
• Multi-thread e clusterização sem alterar uma linha de
código
pm2
(https://github.com/Unitech/pm2)
• Roda e gerencia os processos do Node.js (mantém
processo rodando para sempre)
• Reload no código on-the-fly (zero downtime)
• Multi-thread e clusterização sem alterar uma linha de
código
• Monitoramento e gerenciamento dos logs
pm2
(https://github.com/Unitech/pm2)
• Roda e gerencia os processos do Node.js (mantém
processo rodando para sempre)
• Reload no código on-the-fly (zero downtime)
• Multi-thread e clusterização sem alterar uma linha de
código
• Monitoramento e gerenciamento dos logs
• API RESTful + interface web
Nível 6
$ pm2 reload all
Strider é o servidor de CI e o pm2 reinicia o processo on-the-fly,
sem perder nenhum request
Strider (servidor de CI)
+
Nível 7 (ChatOps)
$ pm2 reload all
Janky é o servidor de CI, Heaven é o servidor de deployment e o
pm2 reinicia o processo on-the-fly, sem perder nenhum request
Janky (Jenkins) + Heaven + Hubot
+
ChatOps
ChatOps
• Todos os commits são testados (via Janky + Jenkins)
ChatOps
• Todos os commits são testados (via Janky + Jenkins)
• Todas as branches têm um estado (green/no-green)
ChatOps
• Todos os commits são testados (via Janky + Jenkins)
• Todas as branches têm um estado (green/no-green)
• Todas as branches green podem ser deployadas (sem
regressão)
ChatOps
• Todos os commits são testados (via Janky + Jenkins)
• Todas as branches têm um estado (green/no-green)
• Todas as branches green podem ser deployadas (sem
regressão)
• Tudo acontece integrado ao GitHub
ChatOps
• Todos os commits são testados (via Janky + Jenkins)
• Todas as branches têm um estado (green/no-green)
• Todas as branches green podem ser deployadas (sem
regressão)
• Tudo acontece integrado ao GitHub
• Deploy da branch X (via Heaven) para um servidor ou
grupo de servidores
ChatOps
• Todos os commits são testados (via Janky + Jenkins)
• Todas as branches têm um estado (green/no-green)
• Todas as branches green podem ser deployadas (sem
regressão)
• Tudo acontece integrado ao GitHub
• Deploy da branch X (via Heaven) para um servidor ou
grupo de servidores
• Tudo acontece numa sala de bate papo com todas as
pessoas relacionadas ao projeto.
$ hubot ci build pagarme-api/boleto_upgrade
$ hubot ci build pagarme-api/boleto_upgrade
$ hubot deploy pagarme-api/boleto_upgrade to prod-test/api01
$ hubot ci build pagarme-api/boleto_upgrade
$ hubot deploy pagarme-api/boleto_upgrade to prod-test/api01
$ alias sharon=hubot
$ alias sharon=hubot
$ sharon deploy pagarme-api/boleto_upgrade to prod-test/api01
$ sharon ci build pagarme-api/boleto_upgrade
Conclusões…
PEDROFRANCESCHI
@pedroh96
pedro@pagar.me
github.com/pedrofranceschi
Obrigado! :)
PEDROFRANCESCHI
@pedroh96
pedro@pagar.me
github.com/pedrofranceschi
Node.js no

Node.js no Pagar.me

  • 1.
  • 2.
  • 3.
  • 4.
    • O problema •Por que Node.js? Assuntos
  • 5.
    • O problema •Por que Node.js? • Problemas de Node.js Assuntos
  • 6.
    • O problema •Por que Node.js? • Problemas de Node.js • Node.js“the right way” Assuntos
  • 7.
    • O problema •Por que Node.js? • Problemas de Node.js • Node.js“the right way” • Repensando a infraestrutura (+ Microservices) Assuntos
  • 8.
    • O problema •Por que Node.js? • Problemas de Node.js • Node.js“the right way” • Repensando a infraestrutura (+ Microservices) • Deployment (+ Continuous Integration) Assuntos
  • 9.
    • O problema •Por que Node.js? • Problemas de Node.js • Node.js“the right way” • Repensando a infraestrutura (+ Microservices) • Deployment (+ Continuous Integration) • Conclusões… Assuntos
  • 10.
  • 11.
    “Montar um gatewayde pagamentos amigável para desenvolvedores e empreendedores”
  • 12.
    “A forma maissimples de receber pagamentos online”
  • 13.
  • 14.
  • 15.
    • Simplicidade: RESTful+ JSON Premissas da API
  • 16.
    • Simplicidade: RESTful+ JSON • Ambiente de testes isolado e decente Premissas da API
  • 17.
    • Simplicidade: RESTful+ JSON • Ambiente de testes isolado e decente • Segurança sem comprometer simplicidade (PCI) Premissas da API
  • 18.
    • Simplicidade: RESTful+ JSON • Ambiente de testes isolado e decente • Segurança sem comprometer simplicidade (PCI) • Uptime de 99,9% Premissas da API
  • 19.
    • Simplicidade: RESTful+ JSON • Ambiente de testes isolado e decente • Segurança sem comprometer simplicidade (PCI) • Uptime de 99,9% • Escalabilidade Premissas da API
  • 20.
  • 21.
    Por que Node.js? Requestde transação API RESTful Sistema antifraude fraude legítimaAdquirente (Cielo, Rede, etc) sucesso/erro erro
  • 22.
    Por que Node.js? Requestde transação API RESTful Sistema antifraude fraude legítimaAdquirente (Cielo, Rede, etc) 10.000 ms 3.000 m s sucesso/erro erro
  • 23.
  • 24.
    Por que Node.js? (nonosso caso) • Requests externos demorados (>1.000ms)
  • 25.
    Por que Node.js? (nonosso caso) • Requests externos demorados (>1.000ms) • I/O intenso em banco de dados
  • 26.
    Por que Node.js? (nonosso caso) • Requests externos demorados (>1.000ms) • I/O intenso em banco de dados • Totalmente assíncrono, single thread e event-based
  • 27.
    Por que Node.js? (nonosso caso) • Requests externos demorados (>1.000ms) • I/O intenso em banco de dados • Totalmente assíncrono, single thread e event-based • Alta carga na aplicação
  • 28.
    Por que Node.js? (nonosso caso) • Requests externos demorados (>1.000ms) • I/O intenso em banco de dados • Totalmente assíncrono, single thread e event-based • Alta carga na aplicação • Pouco processamento (sem blocking de CPU)
  • 29.
    Por que Node.js? (nonosso caso) “Mas é só usar threads em qualquer linguagem!..”
  • 30.
    Por que Node.js? (nonosso caso) “Mas é só usar threads em qualquer linguagem!..” Não.
  • 31.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1.000 threads por segundo
  • 32.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1.000 threads por segundo … se cada request leva em média 10 segundos …
  • 33.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1.000 threads por segundo … se cada request leva em média 10 segundos … Em 9 segundos, teremos 9.000 threads
  • 34.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1.000 threads por segundo … se cada request leva em média 10 segundos … Em 9 segundos, teremos 9.000 threads Isso escala? :P :P :P
  • 35.
    Por que Node.js? (nonosso caso) Agora, em Node.js…
  • 36.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1 thread
  • 37.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1 thread … se cada request leva em média 10 segundos …
  • 38.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1 thread … se cada request leva em média 10 segundos … Em 9 segundos, teremos 1 thread
  • 39.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1 thread … se cada request leva em média 10 segundos … Em 9 segundos, teremos 1 thread Isso escala? Sim.
  • 40.
    Por que Node.js? (nonosso caso) 1.000 requests por segundo com conexão a uma API externa + I/O no DB = 1 thread … se cada request leva em média 10 segundos … Em 9 segundos, teremos 1 thread Isso escala? Sim. #eventloopFTW
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
    I/O síncrona Aplicação Sistema operacional I/O retorno I/Obloqueante (aplicação travada) I/O retorno I/O bloqueante (aplicação travada)
  • 48.
    I/O síncrona Aplicação Sistema operacional I/O retorno I/Obloqueante (aplicação travada) I/O retorno I/O bloqueante (aplicação travada) I/O retorno I/O bloqueante (aplicação travada)
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
    I/O“assíncrona”com threads Aplicação Sistema operacional I/O Thread callback I/Obloqueante (thread travada) I/O Thread callback I/O bloqueante (thread travada)
  • 59.
    I/O“assíncrona”com threads Aplicação Sistema operacional I/O Thread callback I/Obloqueante (thread travada) I/O Thread callback I/O bloqueante (thread travada) I/O Thread callback I/O bloqueante (thread travada)
  • 60.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional
  • 61.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional I/O
  • 62.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional I/O
  • 63.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional I/O
  • 64.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional I/O
  • 65.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… I/O
  • 66.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS I/O
  • 67.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS I/O
  • 68.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS I/O
  • 69.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS I/O
  • 70.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS I/O callback
  • 71.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS I/O callback I/O
  • 72.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS …………………………………… I/O callback I/O
  • 73.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS …………………………………… I/O callback I/O I/O
  • 74.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS ………………………………………………………………………… I/O callback I/O I/O
  • 75.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS ………………………………………………………………………… I/O callback I/O I/O callback
  • 76.
    O segredo doNode.js JavaScript (event loop) V8 libuv Sistema operacional …………………………………… operações assíncronas em nível de OS ………………………………………………………………………… I/O callback I/O I/O callback callback
  • 77.
    Node.js escala emI/O bloqueante, não em utilização de CPU (threads são boas em processamento paralelo)
  • 80.
  • 81.
    Código assíncrono (raceconditions, callback hell, testes assíncronos, etc) db.query("SELECT a FROM users WHERE ...;", function (err, result1) { db.query("SELECT b FROM users WHERE ...;", function (err, result2) { db.query("SELECT c FROM users WHERE ...;", function (err, result3) { db.query("SELECT d FROM users WHERE ...;", function (err, result4) { db.query("SELECT e FROM users WHERE ...;", function (err, result5) { console.log("Finished."); }); }); }); }); }); Um ótimo exemplo do que não fazer: callback hell. Problemas de Node.js
  • 82.
    Problemas de Javascript:bizarrices e facilidade em não seguir padrões e orientação a objetos. > 0.1+0.2 0.30000000000000004 > typeof NaN 'number' > NaN === NaN false Cortesia do wtfjs.com Problemas de Node.js
  • 83.
    Exceptions não tratadasmatam o processo. var name = “Pedro Franceschi"; console.log("Tamanho do primeiro nome: " + name.split(" ")[0].length); console.log("Tamanho do segundo nome: " + name.split(" ")[1].length); Problemas de Node.js $ node test.js Tamanho do primeiro nome: 5 Tamanho do segundo nome: 10
  • 84.
    Exceptions não tratadasmatam o processo. var name = “Pedro"; console.log("Tamanho do primeiro nome: " + name.split(" ")[0].length); console.log("Tamanho do segundo nome: " + name.split(" ")[1].length); Problemas de Node.js $ node test.js Tamanho do primeiro nome: 5 /private/tmp/test.js:4 console.log("Tamanho do segundo nome: " + name.split(" ")[1].length); ^ TypeError: Cannot read property 'length' of undefined at Object.<anonymous> (/private/tmp/test.js:4:61) at Module._compile (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) at startup (node.js:119:16) at node.js:901:3
  • 85.
  • 86.
    Problemas de Node.js •Single thread (escalar horizontalmente e verticalmente com múltiplas instâncias)
  • 87.
    Problemas de Node.js •Single thread (escalar horizontalmente e verticalmente com múltiplas instâncias) • Leaks de memória difíceis de detectar (back-end em C++, variáveis globalizadas, closures, etc)
  • 88.
    Problemas de Node.js •Single thread (escalar horizontalmente e verticalmente com múltiplas instâncias) • Leaks de memória difíceis de detectar (back-end em C++, variáveis globalizadas, closures, etc) • Programadores front-end mexendo em back-end (“é tudo Javascript!!”)
  • 89.
    Problemas de Node.js •Single thread (escalar horizontalmente e verticalmente com múltiplas instâncias) • Leaks de memória difíceis de detectar (back-end em C++, variáveis globalizadas, closures, etc) • Programadores front-end mexendo em back-end (“é tudo Javascript!!”) • Existe a 6 anos, porém ainda é beta (v0.12.0) - e às vezes você precisa usar unstable em produção
  • 91.
  • 93.
    Qualidade de vidavs. tempo usando Node
  • 94.
    Qualidade de vidavs. tempo usando Node Node é muito legal!!!
  • 95.
    Qualidade de vidavs. tempo usando Node Node é muito legal!!! Ops… Meu código está ficando uma zona…
  • 96.
    Qualidade de vidavs. tempo usando Node Node é muito legal!!! Ops… Meu código está ficando uma zona… Queria ter feito em Rails…
  • 97.
    Qualidade de vidavs. tempo usando Node Node é muito legal!!! Ops… Meu código está ficando uma zona… Queria ter feito em Rails… MVC de verdade + Promise + Bluebird <3
  • 98.
    Modules Node.js “the rightway” var PI = Math.PI; exports.area = function (r) { return PI * r * r; }; exports.circumference = function (r) { return 2 * PI * r; }; circle.js var circle = require('./circle.js'); console.log( 'The area of a circle of radius 4 is ' + circle.area(4)); main.js
  • 99.
    Node.js “the rightway” describe('Array', function(){ describe('#indexOf()', function(){ it('should return -1 when the value is not present', function(){ [1,2,3].indexOf(5).should.equal(-1); }) }) describe(‘#indexOf() after one second', function(){ before(function(done){ setTimeout(function(){ done(); }, 1000); }); it('should return -1 when the value is not present', function(){ [1,2,3].indexOf(5).should.equal(-1); }) }) }); Testes (JavaScript quebra) (https://github.com/visionmedia/mocha e https://github.com/visionmedia/should.js/)
  • 100.
    Node.js “the rightway” Models (Sequelize/Mongoose) var Transaction = Sequelize.define('Transaction', { amount: Sequelize.INTEGER, cost: Sequelize.FLOAT, status: { type: Sequelize.ENUM, values: ['paid', 'refused', 'refunded'], defaultValue: 'processing' } }, { instanceMethods: { calculateCost: function(status){ status = status || this.status; if(status == 'paid') { return 50 + (this.amount * 0.015); } }, }, classMethods: { }, hooks: { beforeUpdate: function(transaction) { return transaction.calculateCost() .then(function(cost) { transaction.cost = cost; }); } }, });
  • 101.
    Node.js “the rightway” Models (Sequelize/Mongoose) var Transaction = Sequelize.define('Transaction', { amount: Sequelize.INTEGER, cost: Sequelize.FLOAT, status: { type: Sequelize.ENUM, values: ['paid', 'refused', 'refunded'], defaultValue: 'processing' } }, { instanceMethods: { calculateCost: function(status){ status = status || this.status; if(status == 'paid') { return 50 + (this.amount * 0.015); } }, }, classMethods: { }, hooks: { beforeUpdate: function(transaction) { return transaction.calculateCost() .then(function(cost) { transaction.cost = cost; }); } }, });
  • 102.
    Node.js “the rightway” Models (Sequelize/Mongoose) var Transaction = Sequelize.define('Transaction', { amount: Sequelize.INTEGER, cost: Sequelize.FLOAT, status: { type: Sequelize.ENUM, values: ['paid', 'refused', 'refunded'], defaultValue: 'processing' } }, { instanceMethods: { calculateCost: function(status){ status = status || this.status; if(status == 'paid') { return 50 + (this.amount * 0.015); } }, }, classMethods: { }, hooks: { beforeUpdate: function(transaction) { return transaction.calculateCost() .then(function(cost) { transaction.cost = cost; }); } }, });
  • 103.
    Node.js “the rightway” Models (Sequelize/Mongoose) var Transaction = Sequelize.define('Transaction', { amount: Sequelize.INTEGER, cost: Sequelize.FLOAT, status: { type: Sequelize.ENUM, values: ['paid', 'refused', 'refunded'], defaultValue: 'processing' } }, { instanceMethods: { calculateCost: function(status){ status = status || this.status; if(status == 'paid') { return 50 + (this.amount * 0.015); } }, }, classMethods: { }, hooks: { beforeUpdate: function(transaction) { return transaction.calculateCost() .then(function(cost) { transaction.cost = cost; }); } }, });
  • 104.
    Node.js “the rightway” Models (Sequelize/Mongoose) var Transaction = Sequelize.define('Transaction', { amount: Sequelize.INTEGER, cost: Sequelize.FLOAT, status: { type: Sequelize.ENUM, values: ['paid', 'refused', 'refunded'], defaultValue: 'processing' } }, { instanceMethods: { calculateCost: function(status){ status = status || this.status; if(status == 'paid') { return 50 + (this.amount * 0.015); } }, }, classMethods: { }, hooks: { beforeUpdate: function(transaction) { return transaction.calculateCost() .then(function(cost) { transaction.cost = cost; }); } }, });
  • 105.
    Node.js “the rightway” Promises (Bluebird) fs.readFile("file.json", function(err, val) { if(err) { console.error("unable to read file"); } else { try { val = JSON.parse(val); console.log(val.success); } catch(e) { console.error("invalid json in file"); } } });
  • 106.
    Node.js “the rightway” Promises (Bluebird) fs.readFile("file.json", function(err, val) { if(err) { console.error("unable to read file"); } else { try { val = JSON.parse(val); console.log(val.success); } catch(e) { console.error("invalid json in file"); } } });
  • 107.
    Node.js “the rightway” Promises (Bluebird) fs.readFileAsync("file.json").then(JSON.parse).then(function(val) { console.log(val.success); }) .catch(SyntaxError, function(e) { console.error("invalid json in file"); }) .catch(function(e){ console.error("unable to read file") }); Assim é bem melhor :)
  • 108.
    Node.js “the rightway” Lodash var lines = [ ['name', 'type', 'cost'], ['iPhone', 'cellphone', '2000'], ['MacBook', 'computer', '10000'], ['iPad', 'tablet', '1500'] ]; var joinedLines = []; for(var i = 0; i < lines.length; i++) { joinedLines.push(lines[i].join(',')); } var csvContent = joinedLines.join('n'); name,type,cost iPhone,cellphone,2000 MacBook,computer,10000 iPad,tablet,1500 Output
  • 109.
    Node.js “the rightway” Lodash var lines = [ ['name', 'type', 'cost'], ['iPhone', 'cellphone', '2000'], ['MacBook', 'computer', '10000'], ['iPad', 'tablet', '1500'] ]; var joinedLines = []; for(var i = 0; i < lines.length; i++) { joinedLines.push(lines[i].join(',')); } var csvContent = joinedLines.join('n'); name,type,cost iPhone,cellphone,2000 MacBook,computer,10000 iPad,tablet,1500 Output
  • 110.
    Node.js “the rightway” Lodash var lines = [ ['name', 'type', 'cost'], ['iPhone', 'cellphone', '2000'], ['MacBook', 'computer', '10000'], ['iPad', 'tablet', '1500'] ]; var joinedLines = []; for(var i = 0; i < lines.length; i++) { joinedLines.push(lines[i].join(',')); } var csvContent = joinedLines.join('n'); name,type,cost iPhone,cellphone,2000 MacBook,computer,10000 iPad,tablet,1500 Output
  • 111.
    Node.js “the rightway” Assim é bem melhor :) Lodash var _ = require('lodash'); var lines = [ ['name', 'type', 'cost'], ['iPhone', 'cellphone', '2000'], ['MacBook', 'computer', '10000'], ['iPad', 'tablet', '1500'] ]; var csvContent = _.map(lines, function(line){ return line.join(','); }).join('n'); name,type,cost iPhone,cellphone,2000 MacBook,computer,10000 iPad,tablet,1500 Output
  • 112.
  • 113.
    Node.js “the rightway” • Seguir e manter um code style (dica: Google JavaScript Style Guide)
  • 114.
    Node.js “the rightway” • Seguir e manter um code style (dica: Google JavaScript Style Guide) • Tratamento de erros consistente via Promise (evitar que processos morram)
  • 115.
    Node.js “the rightway” • Seguir e manter um code style (dica: Google JavaScript Style Guide) • Tratamento de erros consistente via Promise (evitar que processos morram) • Manter consistência entre as bibliotecas de Promise (i.e. usar a mesma do wrapper do database)
  • 116.
    Node.js “the rightway” • Seguir e manter um code style (dica: Google JavaScript Style Guide) • Tratamento de erros consistente via Promise (evitar que processos morram) • Manter consistência entre as bibliotecas de Promise (i.e. usar a mesma do wrapper do database) • Sem for(var i = 0; i < object.length; i++) - #lodashFTW
  • 117.
  • 118.
    Node.js “the rightway” utils • Express.js: lightweight HTTP framework
  • 119.
    Node.js “the rightway” utils • Express.js: lightweight HTTP framework • Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc.
  • 120.
    Node.js “the rightway” utils • Express.js: lightweight HTTP framework • Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc. • Mongoose: ORM de MongoDB
  • 121.
    Node.js “the rightway” utils • Express.js: lightweight HTTP framework • Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc. • Mongoose: ORM de MongoDB • Commander: wrapper de command line
  • 122.
    Node.js “the rightway” utils • Express.js: lightweight HTTP framework • Sequelize: ORM de MySQL, PostgreSQL, sqlite3, etc. • Mongoose: ORM de MongoDB • Commander: wrapper de command line • Vim: melhor editor de texto :P
  • 123.
  • 124.
    Infraestrutura do Pagar.me Router api.pagar.me Servidorda API (Node.js) Ambiente de testes (sandbox dos clientes) Ambiente de produção Servidor da API (Node.js)
  • 125.
    Infraestrutura do Pagar.me Router api.pagar.me Servidorda API (Node.js) ElasticSearchElasticSearch Ambiente de testes (sandbox dos clientes) Ambiente de produção Servidor da API (Node.js)
  • 126.
    Infraestrutura do Pagar.me Router api.pagar.me Servidorda API (Node.js) ElasticSearchElasticSearch MySQL (transações e dados relacionais) MySQL (transações e dados relacionais) Ambiente de testes (sandbox dos clientes) Ambiente de produção Servidor da API (Node.js)
  • 127.
    Infraestrutura do Pagar.me Router api.pagar.me Servidorda API (Node.js) ElasticSearchElasticSearch MySQL (transações e dados relacionais) MySQL (transações e dados relacionais) Ambiente de testes (sandbox dos clientes) Ambiente de produção Servidor da API (Node.js) Redis + Redis +
  • 128.
    Infraestrutura do Pagar.me Router api.pagar.me Servidorda API (Node.js) ElasticSearchElasticSearch MySQL (transações e dados relacionais) MySQL (transações e dados relacionais) MongoDB (dados de clientes e não relacionais) Ambiente de testes (sandbox dos clientes) Ambiente de produção Servidor da API (Node.js) Redis + Redis +
  • 129.
  • 130.
    Por que tantosbancos? • Separar dados de teste (sandbox dos clientes) dos dados de produção
  • 131.
    Por que tantosbancos? • Separar dados de teste (sandbox dos clientes) dos dados de produção • MySQL: dados relacionais (transações, assinaturas, planos, cartões, etc.)
  • 132.
    Por que tantosbancos? • Separar dados de teste (sandbox dos clientes) dos dados de produção • MySQL: dados relacionais (transações, assinaturas, planos, cartões, etc.) • MongoDB: dados não-relacionais (informações do cliente, usuários de uma conta, etc.)
  • 133.
    Por que tantosbancos? • Separar dados de teste (sandbox dos clientes) dos dados de produção • MySQL: dados relacionais (transações, assinaturas, planos, cartões, etc.) • MongoDB: dados não-relacionais (informações do cliente, usuários de uma conta, etc.) • ElasticSearch: indexação/buscas ultra-rápidas (expondo uma engine de buscas poderosa para os clientes)
  • 134.
    Por que tantosbancos? • Separar dados de teste (sandbox dos clientes) dos dados de produção • MySQL: dados relacionais (transações, assinaturas, planos, cartões, etc.) • MongoDB: dados não-relacionais (informações do cliente, usuários de uma conta, etc.) • ElasticSearch: indexação/buscas ultra-rápidas (expondo uma engine de buscas poderosa para os clientes) • Redis: fila para notificações entre serviços
  • 135.
    Por que tantosbancos? • Separar dados de teste (sandbox dos clientes) dos dados de produção • MySQL: dados relacionais (transações, assinaturas, planos, cartões, etc.) • MongoDB: dados não-relacionais (informações do cliente, usuários de uma conta, etc.) • ElasticSearch: indexação/buscas ultra-rápidas (expondo uma engine de buscas poderosa para os clientes) • Redis: fila para notificações entre serviços • Não há porque se prender a uma tecnologia quando cada uma delas resolve uma parte do seu problema
  • 136.
    Infraestrutura do Pagar.me Router api.pagar.me Servidorda API (Node.js) ElasticSearchElasticSearch MySQL (transações e dados relacionais) MySQL (transações e dados relacionais) MongoDB (dados de clientes e não relacionais) Ambiente de testes (sandbox dos clientes) Ambiente de produção Servidor da API (Node.js) Redis + Redis +
  • 137.
    Infraestrutura do Pagar.me Router api.pagar.me Servidorda API (Node.js) ElasticSearchElasticSearch MySQL (transações e dados relacionais) MySQL (transações e dados relacionais) MongoDB (dados de clientes e não relacionais) Ambiente de testes (sandbox dos clientes) Ambiente de produção Servidor da API (Node.js) Redis + Redis +
  • 138.
  • 140.
  • 141.
    Cliente node api.js -p3000 node gateway.js -p 5000
  • 142.
    Cliente node api.js -p3000 node gateway.js -p 5000 API RESTful (pagar.me/docs)
  • 143.
    Cliente node api.js -p3000 node gateway.js -p 5000 API RESTful (pagar.me/docs) API RESTful (serviço interno)
  • 144.
    Cliente node api.js -p3000 node gateway.js -p 5000 API RESTful (pagar.me/docs) XML, SOAP, ISO 8583, X25, sinal de fumaça… :P API RESTful (serviço interno)
  • 145.
    Cliente node api.js -p3000 node gateway.js -p 5000 API RESTful (pagar.me/docs) XML, SOAP, ISO 8583, X25, sinal de fumaça… :P ~ 550 ms API RESTful (serviço interno)
  • 146.
    Cliente node api.js -p3000 node gateway.js -p 5000 API RESTful (pagar.me/docs)~ 500ms XML, SOAP, ISO 8583, X25, sinal de fumaça… :P ~ 550 ms API RESTful (serviço interno)
  • 147.
    Cliente node api.js -p3000 node gateway.js -p 5000 API RESTful (pagar.me/docs)~ 500ms XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms ~ 550 ms API RESTful (serviço interno)
  • 148.
    Cliente node api.js -p3000 node gateway.js -p 5000 node hookshot.js API RESTful (pagar.me/docs)~ 500ms XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms ~ 550 ms API RESTful (serviço interno)
  • 149.
    Cliente node api.js -p3000 node gateway.js -p 5000 node hookshot.js API RESTful (pagar.me/docs)~ 500ms Redis (queue) XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms ~ 550 ms API RESTful (serviço interno)
  • 150.
    Cliente node api.js -p3000 node gateway.js -p 5000 node hookshot.js API RESTful (pagar.me/docs)~ 500ms Redis (queue) XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms ~ 550 ms API RESTful (serviço interno) Request HTTP para o servidor do cliente
  • 151.
    Cliente node api.js -p3000 node gateway.js -p 5000 node hookshot.js API RESTful (pagar.me/docs)~ 500ms Redis (queue) XML, SOAP, ISO 8583, X25, sinal de fumaça… :P> 5000 ms ~ 550 ms API RESTful (serviço interno) ~ 100 ms Request HTTP para o servidor do cliente
  • 152.
  • 153.
    Nível 1 $ nodeserver.js n00bz… Processo não roda em background
  • 154.
    Nível 2 $ gitpull && npm install && node server.js & Opa… Agora tem Git, update das dependências pelo NPM e o processo roda em background
  • 155.
    Nível 3 $ gitpull && npm install && nohup node server.js Nohup roda o processo mesmo depois do logout do SSH
  • 156.
    Nível 4 $ gitpull && npm install && service node-server restart Um serviço é responsável por rodar e reiniciar o processo e salvar os logs do processo
  • 157.
    Nível 5 $ servicenode-server restart Agora o servidor de CI lida com o Git e as dependências Servidor de Continuous Integration (CI) +
  • 158.
  • 159.
    pm2 (https://github.com/Unitech/pm2) • Roda egerencia os processos do Node.js (mantém processo rodando para sempre)
  • 160.
    pm2 (https://github.com/Unitech/pm2) • Roda egerencia os processos do Node.js (mantém processo rodando para sempre) • Reload no código on-the-fly (zero downtime)
  • 161.
    pm2 (https://github.com/Unitech/pm2) • Roda egerencia os processos do Node.js (mantém processo rodando para sempre) • Reload no código on-the-fly (zero downtime) • Multi-thread e clusterização sem alterar uma linha de código
  • 162.
    pm2 (https://github.com/Unitech/pm2) • Roda egerencia os processos do Node.js (mantém processo rodando para sempre) • Reload no código on-the-fly (zero downtime) • Multi-thread e clusterização sem alterar uma linha de código • Monitoramento e gerenciamento dos logs
  • 163.
    pm2 (https://github.com/Unitech/pm2) • Roda egerencia os processos do Node.js (mantém processo rodando para sempre) • Reload no código on-the-fly (zero downtime) • Multi-thread e clusterização sem alterar uma linha de código • Monitoramento e gerenciamento dos logs • API RESTful + interface web
  • 164.
    Nível 6 $ pm2reload all Strider é o servidor de CI e o pm2 reinicia o processo on-the-fly, sem perder nenhum request Strider (servidor de CI) +
  • 165.
    Nível 7 (ChatOps) $pm2 reload all Janky é o servidor de CI, Heaven é o servidor de deployment e o pm2 reinicia o processo on-the-fly, sem perder nenhum request Janky (Jenkins) + Heaven + Hubot +
  • 166.
  • 167.
    ChatOps • Todos oscommits são testados (via Janky + Jenkins)
  • 168.
    ChatOps • Todos oscommits são testados (via Janky + Jenkins) • Todas as branches têm um estado (green/no-green)
  • 169.
    ChatOps • Todos oscommits são testados (via Janky + Jenkins) • Todas as branches têm um estado (green/no-green) • Todas as branches green podem ser deployadas (sem regressão)
  • 170.
    ChatOps • Todos oscommits são testados (via Janky + Jenkins) • Todas as branches têm um estado (green/no-green) • Todas as branches green podem ser deployadas (sem regressão) • Tudo acontece integrado ao GitHub
  • 171.
    ChatOps • Todos oscommits são testados (via Janky + Jenkins) • Todas as branches têm um estado (green/no-green) • Todas as branches green podem ser deployadas (sem regressão) • Tudo acontece integrado ao GitHub • Deploy da branch X (via Heaven) para um servidor ou grupo de servidores
  • 172.
    ChatOps • Todos oscommits são testados (via Janky + Jenkins) • Todas as branches têm um estado (green/no-green) • Todas as branches green podem ser deployadas (sem regressão) • Tudo acontece integrado ao GitHub • Deploy da branch X (via Heaven) para um servidor ou grupo de servidores • Tudo acontece numa sala de bate papo com todas as pessoas relacionadas ao projeto.
  • 174.
    $ hubot cibuild pagarme-api/boleto_upgrade
  • 175.
    $ hubot cibuild pagarme-api/boleto_upgrade $ hubot deploy pagarme-api/boleto_upgrade to prod-test/api01
  • 176.
    $ hubot cibuild pagarme-api/boleto_upgrade $ hubot deploy pagarme-api/boleto_upgrade to prod-test/api01 $ alias sharon=hubot
  • 177.
    $ alias sharon=hubot $sharon deploy pagarme-api/boleto_upgrade to prod-test/api01 $ sharon ci build pagarme-api/boleto_upgrade
  • 180.
  • 182.
  • 183.