Mais conteúdo relacionado Semelhante a Real World Lessons on the Pain Points of Node.JS Application (20) Real World Lessons on the Pain Points of Node.JS Application1. Real World Lessons on the Pain Points of
Node.js Applications
@Ben_Hall
Ben@BenHall.me.uk
OcelotUproar.com / Katacoda.com
4. Agenda
• Creating strong foundation
– Node v6/v7, NPM, Security
• Error Handling
• Async / Promises
• Deploying / Scaling
• Performance
• Debugging
9. #nodestats for March: Node 6.x hits 48% share, Node 7.x breaks 15%, 0.10 and 0.12 drop
below 10% share for the first time:
https://mobile.twitter.com/i/web/status/838922927735132160
12. Node Release Stages
• CURRENT: new features (and bug fixes and
security patches)
• ACTIVE LTS: bug fixes (and security patches)
• MAINTENANCE: only security patches
13. Docker to test deployment
• Didn’t need to install anything on host
> docker run -it -v $(pwd):/src -p 3000 node:6
root@container:> npm install
root@container:> npm start
18. Angular 1.2 => 1.3
> angular.element(document)
[#document]
> angular.element(document)
TypeError: undefined is not a function
31. Child Process Exec
child_process.exec(req.query.url, function (err, data) {
console.log(data);
});
https://localhost:49155/api/openUrlInDefaultBrowser?url=c:/windows/sy
stem32/calc.exe
Thanks TrendMicro Antivirus on Windows!
https://code.google.com/p/google-security-research/issues/detail?id=693
32. Cross-Site Request Forgery
var csrf = require('csurf');
var csrfProtection = csrf({ cookie: true });
var parseForm = bodyParser.urlencoded({ extended: false });
app.get('/form', csrfProtection, function(req, res) {
res.render('send', { csrfToken: req.csrfToken() });
});
app.post('/process', parseForm, csrfProtection, function(req, res) {
res.send('data is being processed');
});
https://blog.risingstack.com/node-js-security-checklist/
33. Rate Limiting
var ratelimit = require('koa-ratelimit');
var ipBasedRatelimit = ratelimit({
db: redis.createClient(),
duration: 60000,
max: 10,
id: function (context) {
return context.ip;
}
});
app.post('/login', ipBasedRatelimit, handleLogin);
https://blog.risingstack.com/node-js-security-checklist/
34. Security Audit NPM Packages
> npm install nsp
> nsp check
(+) 18 vulnerabilities found
35. https://nodesecurity.io/
• Root Path Disclosure (2x)
• Regular Expression Denial of Service (10x)
• Incorrect Handling of Non-Boolean Comparisons
During Minification
• Denial-of-Service Extended Event Loop Blocking
• Denial-of-Service Memory Exhaustion
• Symlink Arbitrary File Overwrite
• Remote Memory Disclosure (2x)
36. NPM Credentials Leaks
• https://github.com/ChALkeR/notes/blob/mast
er/Do-not-underestimate-credentials-
leaks.md
57. “The goal isn’t about removing
levels of indentation but rather
writing modular code that is easy to
reason about”
Strongloop Blog
http://strongloop.com/strongblog/node-js-callback-hell-promises-generators/
67. “You can't get into callback hell if
you don't go there.”
Isaac Schlueter
68. Node 7 – Async Await
class Demo {
async greeting() {
const h = await this.world();
return h;
}
world() {
return Promise.resolve('hello world');
}
}
const retval = await demo.greeting();
69. Node 7 – Async Await Errors
async getPersonFullNameWithTryCatch() {
try {
let response = await fetch('./data/person2.json');
}
catch(e) {
console.log('there was an error');
console.log(e);
}
}
74. Maybe not use Node?
Golang has a great community
Where Node was a few years ago?
77. var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello worldn");
}).listen(8000);
}
78. > NODE_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 online
85. Deploying Node App via Docker
> cat Dockerfile
FROM node:6-onbuild
EXPOSE 3000
> docker build –t my-node-app .
> docker run –p 3000:3000 my-node-app
89. > docker run -d
-p 80:80
-v /var/run/docker.sock:/tmp/docker.sock:ro
jwilder/nginx-proxy
> docker run --name web
-e VIRTUAL_HOST=www.katacoda.com
my-node-app
92. var check_docker = function(cb) {
docker.ping(function(err) { handle_error('docker', err, cb);});
};
var check_redis = function(cb) {
redis.status(function(err, connected) {
if(err === null && connected === "ready") {
cb();
} else {
handle_error('redis', {msg: 'Not Connected', err: err}, cb);
}
})
};
var check_pg = function(cb) {
pg.status(function(err) { handle_error('postgres', err, cb);});
};
98. > npm install v8-profiler
const profiler = require('v8-profiler')
const fs = require('fs')
var profilerRunning = false
function toggleProfiling () {
if (profilerRunning) {
const profile = profiler.stopProfiling()
console.log('stopped profiling')
profile.export()
.pipe(fs.createWriteStream('./myapp-'+Date.now()+'.cpuprofile'))
.once('error', profiler.deleteAllProfiles)
.once('finish', profiler.deleteAllProfiles)
profilerRunning = false
return
}
profiler.startProfiling()
profilerRunning = true
console.log('started profiling')
}
process.on('SIGUSR2', toggleProfiling)
> kill -SIGUSR2 <pid>
114. Summary
• Upgrade to Node.js v6
• Start looking at ES6 / Node 7
• Security
• Manage your errors
• Forgot making promises
• Scale using Docker
Notas do Editor var d = require('domain').create();
d.on('error', function(err){
// handle the error safely
console.log(err);
});
// catch the uncaught errors in this
// asynchronous or synchronous code block
d.run(function(){
// the asynchronous or synchronous code
// that we want to catch thrown errors on
var err = new Error('example');
throw err;
});
var util = require('util');
function UserNameAlreadyExistsError(err) {
Error.call(this);
this.name = 'UserNameAlreadyExistsError';
if(typeof err === 'string') {
this.message = err;
} else {
this.message = err.message;
}
this.detail = err.details;
}
util.inherits(UserNameAlreadyExistsError, Error);
module.exports = UserNameAlreadyExistsError; getTweetsFor("domenic") // promise-returning function
.then(function (tweets) {
var shortUrls = parseTweetsForUrls(tweets);
var mostRecentShortUrl = shortUrls[0];
return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning function
})
.then(httpGet) // promise-returning function
.then(
function (responseBody) {
console.log("Most recent link text:", responseBody);
},
function (error) {
console.error("Error with the twitterverse:", error);
}
); var repo_url = $(site).find('li[data-tab="repo"]').find('a').attr('href');
var activity_url = $(site).find('li[data-tab="activity"]').find('a').attr('href');
request.get("http://github.com" + repo_url, function(rr, repo_html) {
var $_repo = cheerio.load(repo_html);
var repo_list = $_repo('.repolist li');
var results = [];
repo_list.each(function(ix, p) {
var source = true;
if($(p).hasClass('fork'))
source = false;
var repo = { link: "http://github.com" + $(p).find('.repolist-name a').attr('href'), title: $(p).find('.repolist-name').text().clean(), stars: parseInt($(p).find('.stargazers a').text().clean(), 10), is_source: source, language: $(p).find('.language').text()};
if(repo_list.length === (ix + 1)) {
request.get("https://github.com" + activity_url, function(err, activity_html) {
var $_activity = cheerio.load(activity_html);
var repo_list = $_activity('.alert');
var results = [];
repo_list.each(function(ix, p) {
var repo = {title: $(p).find('.title').text().clean(), actioned_at: $(p).find('.js-relative-date').attr('datetime')};
results.push(repo);
if(repo_list.length === (ix + 1)) {
data.repositories = results.repo;
data.public_activity = results.activity;
callback({parser: 'github', id: data.username, profile: data});
}
});
});
}
});
}); var repo_url = $(site).find('li[data-tab="repo"]').find('a').attr('href');
var activity_url = $(site).find('li[data-tab="activity"]').find('a').attr('href');
request.get("http://github.com" + repo_url, function(rr, repo_html) {
var $_repo = cheerio.load(repo_html);
var repo_list = $_repo('.repolist li');
var results = [];
repo_list.each(function(ix, p) {
var source = true;
if($(p).hasClass('fork'))
source = false;
var repo = { link: "http://github.com" + $(p).find('.repolist-name a').attr('href'), title: $(p).find('.repolist-name').text().clean(), stars: parseInt($(p).find('.stargazers a').text().clean(), 10), is_source: source, language: $(p).find('.language').text()};
if(repo_list.length === (ix + 1)) {
request.get("https://github.com" + activity_url, function(err, activity_html) {
var $_activity = cheerio.load(activity_html);
var repo_list = $_activity('.alert');
var results = [];
repo_list.each(function(ix, p) {
var repo = {title: $(p).find('.title').text().clean(), actioned_at: $(p).find('.js-relative-date').attr('datetime')};
results.push(repo);
if(repo_list.length === (ix + 1)) {
data.repositories = results.repo;
data.public_activity = results.activity;
callback({parser: 'github', id: data.username, profile: data});
}
});
});
}
});
}); var repo_url = $(site).find('li[data-tab="repo"]').find('a').attr('href');
var activity_url = $(site).find('li[data-tab="activity"]').find('a').attr('href');
request.get("http://github.com" + repo_url, function(rr, repo_html) {
var $_repo = cheerio.load(repo_html);
var repo_list = $_repo('.repolist li');
var results = [];
repo_list.each(function(ix, p) {
var source = true;
if($(p).hasClass('fork'))
source = false;
var repo = { link: "http://github.com" + $(p).find('.repolist-name a').attr('href'), title: $(p).find('.repolist-name').text().clean(), stars: parseInt($(p).find('.stargazers a').text().clean(), 10), is_source: source, language: $(p).find('.language').text()};
if(repo_list.length === (ix + 1)) {
request.get("https://github.com" + activity_url, function(err, activity_html) {
var $_activity = cheerio.load(activity_html);
var repo_list = $_activity('.alert');
var results = [];
repo_list.each(function(ix, p) {
var repo = {title: $(p).find('.title').text().clean(), actioned_at: $(p).find('.js-relative-date').attr('datetime')};
results.push(repo);
if(repo_list.length === (ix + 1)) {
data.repositories = results.repo;
data.public_activity = results.activity;
callback({parser: 'github', id: data.username, profile: data});
}
});
});
}
});
}); var repo_url = $(site).find('li[data-tab="repo"]').find('a').attr('href');
var activity_url = $(site).find('li[data-tab="activity"]').find('a').attr('href');
request.get("http://github.com" + repo_url, function(rr, repo_html) {
var $_repo = cheerio.load(repo_html);
var repo_list = $_repo('.repolist li');
var results = [];
repo_list.each(function(ix, p) {
var source = true;
if($(p).hasClass('fork'))
source = false;
var repo = { link: "http://github.com" + $(p).find('.repolist-name a').attr('href'), title: $(p).find('.repolist-name').text().clean(), stars: parseInt($(p).find('.stargazers a').text().clean(), 10), is_source: source, language: $(p).find('.language').text()};
if(repo_list.length === (ix + 1)) {
request.get("https://github.com" + activity_url, function(err, activity_html) {
var $_activity = cheerio.load(activity_html);
var repo_list = $_activity('.alert');
var results = [];
repo_list.each(function(ix, p) {
var repo = {title: $(p).find('.title').text().clean(), actioned_at: $(p).find('.js-relative-date').attr('datetime')};
results.push(repo);
if(repo_list.length === (ix + 1)) {
data.repositories = results.repo;
data.public_activity = results.activity;
callback({parser: 'github', id: data.username, profile: data});
}
});
});
}
});
}); var repo_url = $(site).find('li[data-tab="repo"]').find('a').attr('href');
var activity_url = $(site).find('li[data-tab="activity"]').find('a').attr('href');
request.get("http://github.com" + repo_url, function(rr, repo_html) {
var $_repo = cheerio.load(repo_html);
var repo_list = $_repo('.repolist li');
var results = [];
repo_list.each(function(ix, p) {
var source = true;
if($(p).hasClass('fork'))
source = false;
var repo = { link: "http://github.com" + $(p).find('.repolist-name a').attr('href'), title: $(p).find('.repolist-name').text().clean(), stars: parseInt($(p).find('.stargazers a').text().clean(), 10), is_source: source, language: $(p).find('.language').text()};
if(repo_list.length === (ix + 1)) {
request.get("https://github.com" + activity_url, function(err, activity_html) {
var $_activity = cheerio.load(activity_html);
var repo_list = $_activity('.alert');
var results = [];
repo_list.each(function(ix, p) {
var repo = {title: $(p).find('.title').text().clean(), actioned_at: $(p).find('.js-relative-date').attr('datetime')};
results.push(repo);
if(repo_list.length === (ix + 1)) {
data.repositories = results.repo;
data.public_activity = results.activity;
callback({parser: 'github', id: data.username, profile: data});
}
});
});
}
});
}); var parse_repositories = function(repo_url, callback) {
request.get("http://github.com" + repo_url, function(rr, repo_html) {
var $_repo = cheerio.load(repo_html);
var repo_list = $_repo('.repolist li');
var results = [];
repo_list.each(function(ix, p) {
var source = true;
if($_repo(p).hasClass('fork'))
source = false;
var repo = { link: "http://github.com" + $_repo(p).find('.repolist-name a').attr('href'), title: $_repo(p).find('.repolist-name').text().clean(), stars: parseInt($_repo(p).find('.stargazers a').text().clean(), 10), is_source: source, language: $_repo(p).find('.language').text()};
if(repo.title !== '') {
results.push(repo);
}
if(repo_list.length === (ix + 1)) {
callback(null, results);
}
});
if(repo_list.length === 0) {
callback(null, []);
}
});
};
Story Story