处理未捕获的异常

  • 在Node的异步世界中,未捕获的异常是特别需要关注的问题
app.get('/fail', function(req, res){
throw new Error('Nope!');
});
  • 在Express执行路由处理器时,它把它们封装在一个try/catch块中,所以这不是一个真正的未捕获异常。
  • Express会在服务器端记录异常,并且访问者会得到一个丑陋的栈输出。然而服务器是稳定的,其他请求还能得到正确处理。
  • 如果我们想提供一个“好的”错误页面,可以创建文件views/500.handlebars并在所有路由后面添加一个错误处理器:
app.use(function(err, req, res, next){
console.error(err.stack);
app.status(500).render('500');
});
  • 提供一个定制的错误页面总归是一个好的做法;比如,可以在这个错误处理器中发送一封邮件给开发团队,让他们知道网站出错了。
//更糟的情况:

app.get('/epic-fail', function(req, res){
process.nextTick(function(){
throw new Error('Kaboom!');
});
});
  • 它把你的整个服务器都搞垮了。这是因为setTimeout是异步执行的,抛出异常的函数被推迟到Node空闲时才执行。问题是,当Node得到空闲可以执行这个函数时,它已经没有其所服务的请求的上下文了,所以它已经没有资源了,只能毫不客气地关掉整个服务器,因为现在它处于不确定的状态;

  • process.nextTick跟调用没有参数的setTimeout非常像,但它效率更高。我们在这里用它是为了演示,一般你不会在服务器端代码里用它。

  • 可以采取行动处理未捕获的异常,但如果Node不能确定程序的稳定性,也不能。

  • 换句话说,如果出现了未捕获异常,唯一能做的也只是关闭服务器。在这种情况下,最好的做法就是尽可能正常地关闭服务器,并且有个故障转移机制。

  • 最容易的故障转移机制是使用集群。如果你的程序是运行在集群模式下的,当一个工作线程死掉后,主线程会繁衍另一个工作线程来取代它。

关闭服务器

  • Node有两种机制解决这个问题:uncaughtException事件(可能会在将来的Node版本中去掉)和域(推荐)。

  • 一个域基本上是一个执行上下文,它会捕获在其中发生的错误。可以有很多域,可以在处理易出错的代码时创建一个新域。

  • 每个请求都在一个域中处理是一种好的做法,这样就可以追踪那个请求中所有的未捕获错误并做出相应的响应(正常地关闭服务器)。

  • 添加一个中间件就可以非常轻松地满足这个要求。这个中间件应该在所有其他路由或中间件前面

app.use(function(req, res, next){
// 为这个请求创建一个域
var domain = require('domain').create();
// 处理这个域中的错误
domain.on('error', function(err) {
console.error('DOMAIN ERROR CAUGHT\n', err.stack);
try {
// 在5秒内进行故障保护关机
setTimeout(function(){
console.error('Failsafe shutdown.');
process.exit(1);
}, 5000); // 从集群中断开
var worker = require('cluster').worker;
if(worker) worker.disconnect(); // 停止接收新请求
server.close(); try {
// 尝试使用Express错误路由
next(err);
} catch(err) {
// 如果Express错误路由失效,尝试返回普通文本响应
console.error('Express error mechanism failed.\n', err.stack);
res.statusCode = 500;
res.setHeader('content-type', 'text/plain');
res.end('Server error.');
}
} catch(err){
console.error('Unable to send 500 response.\n', err.stack);
}
}); // 向域中添加请求和响应对象
domain.add(req);
domain.add(res); // 执行该域中剩余的请求链
domain.run(next);
}); // 其他中间件和路由放在这里 var server = http.createServer(app).listen(app.get('port'), function(){
console.log('Listening on port %d.', app.get('port'));
});
  • 我们做的第一件事是创建一个域,然后在上面附着一个错误处理器。只要这个域中出现未捕获的错误,就会调用这个函数。
  • 我们在这里采取的方式是试图给任何处理中的请求以恰当的响应,然后关闭服务器。
  • 根据错误的性质,可能无法响应处理中的请求,所以我们首先要确立关闭服务器的截止时间。
  • 在这个例子中,我们允许服务器在5秒内响应处理中的请求(如果它可以)。你所选择的数值取决于你的程序,如果程序经常有长请求,就应该给更多的时间。
  • 一旦确立了截止时间,我们会从集群中断开(如果在集群中),以防止集群给我们分配更多的请求。然后明确告诉服务器我们不再接受新的连接。
  • 最后,我们试图传到错误处理路由(next(err))来响应产生错误的请求。如果那会抛出错误,我们退回去用普通的Node API响应。如果其他的全部失败了,我们会记录错误(客户端得不到响应,最终会超时)。
  • 一旦设置好未处理异常处理器,我们就把请求和响应对象添加到域中(允许那些对象上的所有方法抛出的错误都由域处理)。
  • 最后,我们在域的上下文中运行管道中的下一个中间件。注意,这可以有效地运行域中管道里的所有中间件,因为对next()的调用是链起来的。

一篇介绍的文章

用多台服务器扩展

  • 用集群向外扩展可以实现单台服务器的性能最大化, 但当需要多台服务器时会怎样?这时情况会变得有点复杂。要实现这种并行,需要一台代理服务器(为了跟一般用于访问外部网络的代理区别开,经常被称为反向代理或正向代理,但我发现这种叫法既费解又没必要,所以我只称它为代理)。

  • 在代理领域的两个后起之秀分别是Nginx和HAProxy。还有一些比较小的基于Node的代理服务器,比如proxynode-http-proxy

  • 如果要求不高,或者是用于开发,这些都是很好的选择。对于生产环境而言,我推荐用Nginx或HAProxy (这两个都是免费的,尽管提供服务是收费的)。

  • 如果确实配置了一台代理服务器,请确保告知Express你用了代理,并且它应该得到信任:

app.enable('trust proxy');
  • 这样可以确保req.ipreq.protocolreq.secure能反映客户端和代理服务器之间连接的细节,而不是客户端和你的应用之间的。还有,req.ips将会是一个数组,表明原始客户端IP和所有中间代理的名称或IP地址。

网站监控

  • 网站监控是你可以采取的最重要的(也是最常被忽视的) QA措施之一;

第三方正常运行监控

  • 在网站服务器上正常运行一个监控可能可以发现某些页面不能访问,但如果整个服务器都宕掉了,它甚至可能都发不出一个SOS。

  • 一道防线应该是第三方正常运行监控。UptimeRobot有50个免费监控,并且配置简单。警报可以通过邮件、短信(文本消息)、Twitter或者iPhone应用程序发送。

  • 可以监控单个页面的返回码(除200之外的所有返回码都可以视为错误),或者检查页面上有没有某个关键字。不过要记住,如果用关键字监控,它可能会影响你的分析

应用程序故障

  • 正常运行监控可以非常有效地监测大规模故障。如果用关键字监控,它们甚至可以用来监测应用程序故障。
  • 然而,一般在处理故障时都想表现得更优雅。给用户显示一个友好的消息“对不起,这项服务目前不正常”,并且你会收到一封邮件或一条短信告诉你有故障了。
  • 当你依赖第三方组件时,比如数据库或其他Web服务器,一般会采取这种方式。
  • 一种简单的故障处理方式是有错误时给你自己发邮件;如果通知需求复杂,可能要考虑找一个通知服务,比如亚马逊的简单通知服务(SNS)。

压力测试

  • 压力测试是为了相信服务器可以正常地应对成百上千的并发请求; 压力测试可能非常复杂,并且很大程度上取决于你的项目。

  • 先添加一个简单的测试,确保程序可以满足一秒内对主页的100次请求; 用Node模块loadtest做压力测试:

//qa/tests-stress.js:
var loadtest = require('loadtest');
var expect = require('chai').expect; suite('Stress tests', function(){
test('Homepage should handle 100 requests in a second', function(done){
var options = {
url: 'http://localhost:3000',
concurrency: 4,
maxRequests: 100
};
loadtest.loadTest(options, function(err,result){
expect(!err);
expect(result.totalTimeSeconds < 1);
done();
});
}); });

express-16 与生产相关的问题2的更多相关文章

  1. express-15 与生产相关的问题

    执行环境 Express支持执行环境的概念,它是一种在生产.开发或测试模式中运行应用程序的方法.实际上你可以按自己的想法创建很多种不同的环境. 要记住,开发.生产和测试是"标准"环 ...

  2. Ubuntu 16.04 samba相关配置

    samba是 SMB/CIFS网络协议的重新实现,它作为NFS的补充使得在Linux和Windows系统之间进行文件共享.打印更容易实现. 相关介绍: SAMBA套件: (1)samba:这个套件主要 ...

  3. ubuntu 16.04 mysql 相关

    如何彻底卸载某一版本的数据库 彻底删除ubuntu下的mysql: 1.删除mysql的数据文件 sudo rm /var/lib/mysql/ -R 2.删除mqsql的配置文件 sudo rm / ...

  4. MySQL全面瓦解16:存储过程相关

    概述 大多数SQL语句都是针对一个或多个表的单条语句.但并非所有业务都这么简单,经常会有复杂的操作需要多条语句才能完成. 比如用户购买一个商品,要删减库存表,要生成订单数据,要保存支付信息等等,他是一 ...

  5. Ubuntu 16.10 server 相关

    1)安装图形化界面 sudo apt-get install xinit sudo apt-get install gnome 2)启用root账号 ① sudo passwd root ② 修改/e ...

  6. 16.和input相关的知识点

    1.改变input里面placeholder颜色 <input class="pre_name" type="text" placeholder=&quo ...

  7. SpringBoot中使用mybatis-generator自动生产

    步骤: 1.在pom.xml中添加插件配置 <plugin> <groupId>org.mybatis.generator</groupId> <artifa ...

  8. Express使用手记:核心入门

    入门简介 Express是基于nodejs的web开发框架.优点是易上手.高性能.扩展性强. 易上手:nodejs最初就是为了开发高性能web服务器而被设计出来的,然而相对底层的API会让不少新手望而 ...

  9. 使用express, create-react-app, mongodb搭建react模拟数据开发环境

    提要 最近刚刚完成了一个vue的项目,其中涉及的用户数有6000多个以及其他数据也比较多,为了在前端能够真实的进行数据模拟,所有把全量数据拷贝下来放到了api.json中.这样导致整个api.json ...

随机推荐

  1. Tomcat中文乱码问题的原理和解决方法

    1) 更改 D:\Tomcat\conf\server.xml,指定浏览器的编码格式为“简体中文”: 方法是找到 server.xml 中的 <Connector port="8080 ...

  2. CodeForces 407B Long Path (DP)

    题目链接 题意:一共n+1个房间,一个人从1走到n+1,如果第奇数次走到房间i,会退回到房间Pi,如果偶数次走到房间i,则走到房间i+1,问走到n+1需要多少步,结果对1e9+7取模. 题解:设dp[ ...

  3. IOS - 屏幕适配

    原文:Beginning Auto Layout Tutorial in iOS 7: Part 1 感谢翻译小组成员@answer-huang(博客)热心翻译.如果您有不错的原创或译文,欢迎提交给我 ...

  4. lazyload

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  5. MFC 使用MFC EditBrowse Control控件选择文件或者文件夹

    从工具箱中拖拽一个MFC EditBrowse Control到窗体中, 通过设置“Browse Mode”属性指定“文件浏览”还是“文件夹浏览” 可以通过添加对象的方式将其与一个CString se ...

  6. October 4th 2016 Week 41st Tuesday

    Patience! The windmill never strays in search of the wind. 耐心等待!风车从不跑去找风. Sometimes we need to be pa ...

  7. sql 查询最近30分钟或者自定义时间数据

    ) FROM dual; 30分钟或者自定义

  8. Shell之变量操作

    1.定义变量:为了避免与环境变量冲突,一般用小写:如pp="mydql,kdkfeof"; 第二赋值方案: 调用变量使用$符号: 变量叠加: 2.输出采用echo,如上图所示: 3 ...

  9. 利用 Rational ClearCase ClearMake 构建高性能的企业级构建环境

    转载地址:http://www.ibm.com/developerworks/cn/rational/r-cn-clearmakebuild/ 构建管理是 IBM® Rational® ClearCa ...

  10. JS ——window.onload与$(document).ready()

    我们常常在页面加载完成以后做一些操作,比如一些元素的显示与隐藏.一些动画效果.我们通常有两种方法来完成这个事情,一个就是window.onload事件,另一个就是JQuery的ready()方法.那么 ...