Node.js: serious business
PEDROFRANCESCHI
@pedroh96
pedro@pagar.me
github.com/pedrofranceschi
Assuntos
•

O problema

•

Por que e quando usar Node.js?

•

Problemas de Node.js

•

Node.js “the right way”

•

Repensa...
O problema
“Montar um gateway de pagamentos
amigável para desenvolvedores e
empreendedores”
“A forma mais simples de receber
pagamentos online”
API RESTful

Node.js
C#

Ruby
Python

Dashboard
Angular.js

.NET
Java
PHP
Premissas da API
•

Simplicidade: RESTful + JSON

•

Ambiente de testes isolado e decente

•

Segurança sem comprometer si...
Por que Node.js?
Antes...
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

Adquir...
Por que Node.js?
(no nosso caso)

•

Requests externos demorados (>1.000ms)

•

I/O intenso em banco de dados

•

Totalmen...
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...
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
… se cad...
I/O síncrona
I/O
retorno

I/O
retorno

I/O
retorno

Aplicação

I/O bloqueante
(aplicação travada)

I/O bloqueante
(aplicaç...
I/O “assíncrona” com threads
I/O
callback

I/O
callback

I/O
callback

Aplicação
Thread

Thread

Thread

I/O bloqueante
(t...
O segredo do Node.js
I/O

I/O

callback
callback
callback

I/O

JavaScript (event loop)
V8
libuv
Sistema operacional
……………...
Node.js escala em I/O bloqueante, não
em utilização de CPU
(threads são boas em processamento paralelo)
Se I/O bloqueante não for um problema,
Node.js não tem tantas vantagens.
Problemas de Node.js
Problemas de Node.js
Código assíncrono (race conditions, callback hell,
testes assíncronos, etc)
db.query("SELECT a FROM u...
Problemas de Node.js
Problemas de Javascript: bizarrices e facilidade em não
seguir padrões e orientação a objetos.
> 0.1+...
Problemas de Node.js
Exceptions não tratadas matam o processo.
var name = “Pedro Franceschi";

!

console.log("Tamanho do ...
Problemas de Node.js
Exceptions não tratadas matam o processo.
var name = “Pedro";

!

console.log("Tamanho do primeiro no...
Problemas de Node.js
•

Single thread (escalar horizontalmente e
verticalmente com múltiplas instâncias)

•

Leaks de memó...
Node.js “the right way”
Node.js “the right way”
Modules

var PI = Math.PI;

!

exports.area = function (r) {
return PI * r * r;
};

!

exports.cir...
Node.js “the right way”
Evitar callback hells com async
(https://github.com/caolan/async)

db.query("SELECT a FROM users W...
Node.js “the right way”
Evitar callback hells com async
(https://github.com/caolan/async)
async.parallel({
result1: functi...
Node.js “the right way”
Testes (JavaScript quebra)
(https://github.com/visionmedia/mocha e https://github.com/visionmedia/...
Node.js “the right way”

•

Seguir e manter um code style (dica: Google
JavaScript Style Guide)

•

Tratamento de erros co...
Node.js “the right way”
utils

•

Express.js: lightweight HTTP framework

•

Sequelize: ORM de MySQL, PostgreSQL, sqlite3,...
“Inovação” em padrões de código
não é inovação.
(99,9% das vezes)
“Follow the patterns”
Repensando a infraestrutura
Use o melhor de cada banco de dados.
Infraestrutura do Pagar.me
MySQL

MongoDB

MySQL

(transações e dados relacionais)

(dados de clientes e não relacionais)
...
Por que tantos bancos?
•

Separar dados de teste (sandbox dos clientes) dos dados
de produção

•

MySQL: dados relacionais...
Um parênteses...
ElasticSearch
•

“Open Source Distributed Real Time Search & Analytics”

•

Construído em Java em cima do Apache Lucene (e...
ElasticSearch
Transaction

Customer

amount

name

payment_method

email

card_last_digits document_number
customer_id

do...
ElasticSearch
Transaction

Customer

amount

name

payment_method

email

card_last_digits document_number
customer_id

do...
ElasticSearch
Transaction

Customer

amount

name

payment_method

email

card_last_digits document_number
customer_id

do...
ElasticSearch
(buscando…)
$ curl -X GET ‘http://localhost:9200/pagarme/transaction/_search’ -d '
{
"query": {
"bool": {
"m...
ElasticSearch
• NÃO é um banco de dados. É um indexador.
• Evita I/O no DB (salva JSONs construídos a partir das
queries n...
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 ...
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 proces...
Nível 5
Servidor de Continuous Integration (CI)

+
$ service node-server restart

Agora o servidor de CI lida com o Git e ...
Nível 6
Strider (servidor de CI)

+
$ pm2 reload all

Strider é o servidor de CI e o pm2 reinicia o processo on-the-fly,
se...
pm2
(https://github.com/Unitech/pm2)

•

Roda e gerencia os processos do Node.js (mantém
processo rodando para sempre)

•
...
Strider
(https://github.com/Strider-CD/strider)

•

Servidor de CI 100% em Node.js

•

Integração com Github

•

Monitoram...
Monitoramento
Saber de problemas antes dos
clientes e ser transparente
quando eles acontecerem.
Para saber dos problemas…
!

Pingdom + PagerDuty
Para ser transparente sobre os problemas…
!

Status Page + Twitter de status
Para monitorar possíveis problemas…
!

Librato
Conclusões…
… mas …
JavaScript
Overall…
Obrigado! :)
PEDROFRANCESCHI
@pedroh96
pedro@pagar.me
github.com/pedrofranceschi
Node.js: serious business
PEDROFRANCESCHI
@pedroh96
pedro@pagar.me
github.com/pedrofranceschi
Node.js: serious business
Node.js: serious business
Node.js: serious business
Node.js: serious business
Node.js: serious business
Upcoming SlideShare
Loading in...5
×

Node.js: serious business

8,351

Published on

Por que e como montamos um meio de pagamentos inteiramente em Node.js? Nessa palestra, falarei sobre o que aprendemos construindo um projeto relativamente grande em Node. Primeiro, os motivos que nos levaram a usá-lo no Pagar.me e porque Node foi a ferramenta certa para nós. Depois, sobre como montamos uma infraestrutura que une Node.js com tecnologias diferentes (MongoDB, MySQL e ElasticSearch) para usar a melhor parte de cada uma delas e ganhar versatilidade e escalabilidade.

Também abordarei os cuidados com código (patterns, code style, modules, etc), testes, integração contínua e segurança na stack que precisamos ter para criar algo grande com Node. Além disso, contarei um pouco sobre ele em produção, incluindo as ferramentas que usamos para rodar e monitorar nossa aplicação e a infraestrutura por trás dela. O objetivo é mostrar quando, como e porque utilizar o Node, e provar até onde ele pode chegar.

Published in: Technology
0 Comments
38 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
8,351
On Slideshare
0
From Embeds
0
Number of Embeds
20
Actions
Shares
0
Downloads
117
Comments
0
Likes
38
Embeds 0
No embeds

No notes for slide

Node.js: serious business

  1. 1. Node.js: serious business PEDROFRANCESCHI @pedroh96 pedro@pagar.me github.com/pedrofranceschi
  2. 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…
  3. 3. O problema
  4. 4. “Montar um gateway de pagamentos amigável para desenvolvedores e empreendedores”
  5. 5. “A forma mais simples de receber pagamentos online”
  6. 6. API RESTful Node.js C# Ruby Python Dashboard Angular.js .NET Java PHP
  7. 7. Premissas da API • Simplicidade: RESTful + JSON • Ambiente de testes isolado e decente • Segurança sem comprometer simplicidade • Uptime de 99,9% • Escalabilidade
  8. 8. Por que Node.js?
  9. 9. Antes...
  10. 10. 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
  11. 11. 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)
  12. 12. Por que Node.js? (no nosso caso) “Mas é só usar threads em qualquer linguagem!..” Não.
  13. 13. 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
  14. 14. Por que Node.js? (no nosso caso) Agora, em Node.js…
  15. 15. 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
  16. 16. I/O síncrona I/O retorno I/O retorno I/O retorno Aplicação I/O bloqueante (aplicação travada) I/O bloqueante (aplicação travada) Sistema operacional I/O bloqueante (aplicação travada)
  17. 17. I/O “assíncrona” com threads I/O callback I/O callback I/O callback Aplicação Thread Thread Thread I/O bloqueante (thread travada) I/O bloqueante (thread travada) I/O bloqueante (thread travada) Sistema operacional
  18. 18. 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
  19. 19. Node.js escala em I/O bloqueante, não em utilização de CPU (threads são boas em processamento paralelo)
  20. 20. Se I/O bloqueante não for um problema, Node.js não tem tantas vantagens.
  21. 21. Problemas de Node.js
  22. 22. 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.
  23. 23. 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
  24. 24. 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
  25. 25. 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
  26. 26. 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)
  27. 27. Node.js “the right way”
  28. 28. 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
  29. 29. 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
  30. 30. 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 :)
  31. 31. 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); }) }) });
  32. 32. Node.js “the right way” • Seguir e manter um code style (dica: Google JavaScript Style Guide) • Tratamento de erros consistente (processos morrem)
  33. 33. 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
  34. 34. “Inovação” em padrões de código não é inovação. (99,9% das vezes)
  35. 35. “Follow the patterns”
  36. 36. Repensando a infraestrutura
  37. 37. Use o melhor de cada banco de dados.
  38. 38. 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
  39. 39. 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
  40. 40. Um parênteses...
  41. 41. 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
  42. 42. ElasticSearch Transaction Customer amount name payment_method email card_last_digits document_number customer_id document_type address_id Address { "object": "transaction", "status": "paid", "date_created": "2013-11-21T02:27:10.000Z", "amount": 1000, "installments": 1, "id": 35, "card_holder_name": "PEDRO FRANCESCHI", "card_last_digits": "5592", "card_brand": "visa", "payment_method": "credit_card", "antifraud_score": null, "subscription_id": null, "customer": { "object": "customer", "document_number": "12388451908", "document_type": "cpf", "name": "Pedro Franceschi", "email": null, "born_at": null, "date_created": "2013-11-11T05:24:52.000Z", "id": 1 }, "address": { "object": "address", "street": "Av. Brigadeiro Faria Lima 2941", "street_number": "2941", "neighborhood": “Itaim Bibi", "city": "São Paulo", "state": "SP", "zipcode": "01452000", "country": "Brasil", "id": 1 }, "phone": { "object": "phone", "ddi": "55", "ddd": "21", "number": "88888888", "id": 1 }, "metadata": null Phone customer_id customer_id street ddd neighborhood number id id id phone_id 3 joins para montar o objeto de resposta }
  43. 43. ElasticSearch Transaction Customer amount name payment_method email card_last_digits document_number customer_id document_type address_id Address Phone customer_id customer_id street ddd neighborhood number id id id phone_id ElasticSearch indexa o JSON já montado pelo seu id em um índice (model) específico. $ curl -X POST http://0.0.0.0:9200/pagarme/transactions/35 -d ‘ { "object": "transaction", "status": "paid", "date_created": "2013-11-21T02:27:10.000Z", "amount": 1000, "installments": 1, "id": 35, "card_holder_name": "PEDRO FRANCESCHI", "card_last_digits": "5592", "card_brand": "visa", "payment_method": "credit_card", "antifraud_score": null, "subscription_id": null, "customer": { "object": "customer", "document_number": "12388451908", "document_type": "cpf", "name": "Pedro Franceschi", "email": null, "born_at": null, "date_created": "2013-11-11T05:24:52.000Z", "id": 1 }, "address": { "object": "address", "street": "Av. Brigadeiro Faria Lima 2941", "street_number": "2941", "neighborhood": “Itaim Bibi", "city": "São Paulo", "state": "SP", "zipcode": "01452000", "country": "Brasil", "id": 1 }, "phone": { "object": "phone", "ddi": "55", "ddd": "21", "number": "88888888", "id": 1 }, "metadata": null }’
  44. 44. ElasticSearch Transaction Customer amount name payment_method email card_last_digits document_number customer_id document_type address_id Address Phone customer_id customer_id street ddd neighborhood number id id id phone_id Para retornar o objeto, basta realizar um GET pelo seu id (o objeto é retornado dentro de “_source”) $ curl -X GET http://0.0.0.0:9200/pagarme/transactions/ 35 { "_index": "pagarme", "_type": "transaction", "_id": "35", "_version": 2, "exists": true, "_source": { "object": "transaction", "status": "paid", "date_created": "2013-11-21T02:27:10.000Z", "amount": 1000, "installments": 1, "id": 35, "card_holder_name": “PEDRO FRANCESCHI", "card_last_digits": "5592", "card_brand": "visa", "payment_method": "credit_card", "antifraud_score": null, "subscription_id": null, "customer": { "object": "customer", "document_number": "12388451908", "document_type": "cpf", "name": "Pedro Franceschi", "email": null, "born_at": null, "date_created": "2013-11-11T05:24:52.000Z", "id": 1 }, "address": { "object": "address", "street": "Av. Brigadeiro Faria Lima 2941", "street_number": "2941", "neighborhood": “Itaim Bibi", "city": "São Paulo", "state": "SP", "zipcode": "01452000", "country": "Brasil", "id": 1 }, "phone": { "object": "phone", "ddi": "55", "ddd": "21", "number": "88888888", "id": 1 }, "metadata": null, } }
  45. 45. ElasticSearch (buscando…) $ curl -X GET ‘http://localhost:9200/pagarme/transaction/_search’ -d ' { "query": { "bool": { "must": [ { "text": { "payment_method": "credit_card" } }, { "text": { "address.zipcode": "01452000" } }, { "range": { "amount": { "gt": 1000 } } } ] } } }'
  46. 46. 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)
  47. 47. Deployment
  48. 48. Nível 1 $ node server.js n00bz… Processo não roda em background
  49. 49. 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
  50. 50. Nível 3 $ git pull && npm install && nohup node server.js Nohup roda o processo mesmo depois do logout do SSH
  51. 51. 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
  52. 52. 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
  53. 53. 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
  54. 54. 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
  55. 55. 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.
  56. 56. Monitoramento
  57. 57. Saber de problemas antes dos clientes e ser transparente quando eles acontecerem.
  58. 58. Para saber dos problemas… ! Pingdom + PagerDuty
  59. 59. Para ser transparente sobre os problemas… ! Status Page + Twitter de status
  60. 60. Para monitorar possíveis problemas… ! Librato
  61. 61. Conclusões…
  62. 62. … mas …
  63. 63. JavaScript
  64. 64. Overall…
  65. 65. Obrigado! :) PEDROFRANCESCHI @pedroh96 pedro@pagar.me github.com/pedrofranceschi
  66. 66. Node.js: serious business PEDROFRANCESCHI @pedroh96 pedro@pagar.me github.com/pedrofranceschi
  1. Gostou de algum slide específico?

    Recortar slides é uma maneira fácil de colecionar informações para acessar mais tarde.

×