O documento discute o uso da tecnologia Node.js para construir uma API de pagamentos. Ele explica porque Node.js é adequado para este tipo de aplicação devido à sua natureza assíncrona e orientada a eventos, permitindo lidar com muitas requisições simultâneas de forma escalável. O documento também discute problemas comuns de Node.js e como evitá-los, além de abordar tópicos como infraestrutura, testes e monitoramento.
2. Assuntos
•
O problema
•
Por que e quando usar Node.js?
•
Problemas de Node.js
•
Node.js “the right way”
•
Repensando a infraestrutura
•
Deployment (Continuous Integration) e monitoramento
•
Conclusões…
7. Premissas da API
•
Simplicidade: RESTful + JSON
•
Ambiente de testes isolado e decente
•
Segurança sem comprometer simplicidade
•
Uptime de 99,9%
•
Escalabilidade
11. Por que Node.js?
(Cielo, RedeCard, etc)
legítima
Sistema antifraude
ro
er
o/
ss
ce
su
m
s
10.000 ms
3.
00
0
Adquirente
fraude
erro
API RESTful
Request de transação
12. 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
•
Pouco processamento (sem blocking de CPU)
13. Por que Node.js?
(no nosso caso)
“Mas é só usar threads em qualquer
linguagem!..”
Não.
14. 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
16. 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
19. O segredo do Node.js
I/O
I/O
callback
callback
callback
I/O
JavaScript (event loop)
V8
libuv
Sistema operacional
……………………………………
……………………………………
……………………………………
operações assíncronas em nível de OS
20. Node.js escala em I/O bloqueante, não
em utilização de CPU
(threads são boas em processamento paralelo)
21. Se I/O bloqueante não for um problema,
Node.js não tem tantas vantagens.
24. 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.
25. 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
26. 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);
$ node test.js
Tamanho do primeiro nome: 5
Tamanho do segundo nome: 10
27. Problemas de Node.js
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);
$ 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
28. Problemas de Node.js
•
Single thread (escalar horizontalmente e
verticalmente com múltiplas instâncias)
•
Leaks de memória difíceis de detectar devido a
código mal feito (variáveis globalizadas, closures, etc)
•
Programadores front-end mexendo em back-end
(“é tudo Javascript!!”)
•
Existe a 4 anos, porém ainda é beta (v0.10.22)
30. Node.js “the right way”
Modules
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
31. Node.js “the right way”
Evitar callback hells com async
(https://github.com/caolan/async)
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.");
});
});
});
});
});
Não tente fazer isso em casa
32. Node.js “the right way”
Evitar callback hells com async
(https://github.com/caolan/async)
async.parallel({
result1: function(callback) {
db.query("SELECT a FROM users WHERE ...;", function(err, result1){
callback(err, result1);
});
},
result2: function(callback) {
db.query("SELECT b FROM users WHERE ...;", function(err, result2){
callback(err, result2);
});
},
result3: function(callback) {
db.query("SELECT c FROM users WHERE ...;", function(err, result3){
callback(err, result3);
});
}
}, function(err, results){
console.log("Finished.");
})
Assim é bem melhor :)
33. Node.js “the right way”
Testes (JavaScript quebra)
(https://github.com/visionmedia/mocha e https://github.com/visionmedia/should.js/)
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);
})
})
});
34. Node.js “the right way”
•
Seguir e manter um code style (dica: Google
JavaScript Style Guide)
•
Tratamento de erros consistente (processos morrem)
35. 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
40. Infraestrutura do Pagar.me
MySQL
MongoDB
MySQL
(transações e dados relacionais)
(dados de clientes e não relacionais)
(transações e dados relacionais)
ElasticSearch
ElasticSearch
Servidor da API
Servidor da API
(Node.js)
(Node.js)
Router
Ambiente de produção
Ambiente de testes
(sandbox dos clientes)
api.pagar.me
41. 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)
•
Não há porque se prender a uma tecnologia quando
cada uma delas resolve uma parte do seu problema
43. ElasticSearch
•
“Open Source Distributed Real Time Search & Analytics”
•
Construído em Java em cima do Apache Lucene (engine
robusta de busca e indexação em Java)
•
API RESTful (POST para inserir (indexar) dados num
“model”, GET para buscá-los)
•
Just works and scales (sem instalação, basta iniciar o
servidor; suporte a clusters out of the box)
•
Nosso uso: indexar/buscar responses (incluindo
metadata e nested objects) de models do MySQL
48. ElasticSearch
• NÃO é um banco de dados. É um indexador.
• Evita I/O no DB (salva JSONs construídos a partir das
queries no DB)
• Permite buscas em cima dos próprios objetos que são
retornados para os clientes (padroniza o que é exposto
nos responses independente da estrutura do DB)
• Dashboard em tempo real a partir das indexações
• Dica: crie um script para indexar seu DB (indexar em
tempo real funciona mas pode quebrar)
50. Nível 1
$ node server.js
n00bz… Processo não roda em background
51. 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
52. Nível 3
$ git pull && npm install && nohup node server.js
Nohup roda o processo mesmo depois do logout do SSH
53. 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
54. Nível 5
Servidor de Continuous Integration (CI)
+
$ service node-server restart
Agora o servidor de CI lida com o Git e as dependências
55. Nível 6
Strider (servidor de CI)
+
$ pm2 reload all
Strider é o servidor de CI e o pm2 reinicia o processo on-the-fly,
sem perder nenhum request
56. 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
57. Strider
(https://github.com/Strider-CD/strider)
•
Servidor de CI 100% em Node.js
•
Integração com Github
•
Monitoramento dos testes em tempo real pelo
browser (com Socket.io)
•
Envio de emails com erros nos testes
•
Suporte a testes de regressão, custom scripts de
deployment, etc.