Upload
pedro-franceschi
View
492
Download
0
Tags:
Embed Size (px)
Citation preview
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
RubyNode.js
Python Java
.NET
PHPC#
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
ms
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/Oretorno
I/O bloqueante (aplicação travada)
I/O síncrona
Aplicação
Sistema operacional
I/Oretorno
I/O bloqueante (aplicação travada)
I/Oretorno
I/O bloqueante (aplicação travada)
I/O síncrona
Aplicação
Sistema operacional
I/Oretorno
I/O bloqueante (aplicação travada)
I/Oretorno
I/O bloqueante (aplicação travada)
I/Oretorno
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/Ocallback
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional……………………………………
operações assíncronas em nível de OS
I/Ocallback
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional……………………………………
operações assíncronas em nível de OS
……………………………………
I/Ocallback
I/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional……………………………………
operações assíncronas em nível de OS
……………………………………
I/Ocallback
I/OI/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional……………………………………
operações assíncronas em nível de OS
…………………………………………………………………………
I/Ocallback
I/OI/O
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional……………………………………
operações assíncronas em nível de OS
…………………………………………………………………………
I/Ocallback
I/OI/O
callback
O segredo do Node.js
JavaScript (event loop)
V8
libuv
Sistema operacional……………………………………
operações assíncronas em nível de OS
…………………………………………………………………………
I/Ocallback
I/OI/O
callbackcallback
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 msRequest 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…