处理未捕获的异常

  • 在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. 掌握VS2010调试 -- 入门指南

    1 导言 在软件开发周期中,测试和修正缺陷(defect,defect与bug的区别:Bug是缺陷的一种表现形式,而一个缺陷是可以引起多种Bug的)的时间远多于写代码的时间.通常,debug是指发现缺 ...

  2. 使用iScroll时,input等不能输入内容的解决方法(share)

    最近做移动平台的应用,使用iscroll使屏幕上下滑动.发现当使用iscroll后,input等不能输入内容了.只要在iscroll.js文件中加入如下代码就ok了. function allowFo ...

  3. 【python】lxml

    来源:http://lxml.de/tutorial.html lxml是python中处理xml的一个非常强大的库,可以非常方便的解析和生成xml文件.下面的内容翻译了链接中的一部分 1.生成空xm ...

  4. mongoose学习笔记1--基础知识1

    今天我们将学习Mongoose,什么是Mongoose呢,它于MongoDB又是什么关系呢,它可以用来做什么呢? MongoDB是一个开源的NoSQL数据库,相比MySQL那样的关系型数据库,它更显得 ...

  5. 营业额统计(bzoj1588)

    Description 营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每 ...

  6. 内核中用于数据接收的结构体struct msghdr(转)

    内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...

  7. TCP/IP五层模型

    (2)TCP/IP五层模型的协议   应用层 传输层 网络层 数据链路层 物理层   物理层:中继器.集线器.还有我们通常说的双绞线也工作在物理层 数据链路层:网桥(现已很少使用).以太网交换机(二层 ...

  8. iOS - HTTPS接口加密和身份认证

    为什么要使用HTTPS代替HTTP HTTPS和HTTP的区别 https协议需要到CA申请证书,一般免费证书很少,需要交费. http是超文本传输协议,信息是明文传输,https则是具有安全性的SS ...

  9. C#学习笔记----C#中的闭包机制

    http://www.cnblogs.com/jiejie_peng/p/3701070.html http://www.cnblogs.com/Ribbon/p/3611457.html “ 若匿名 ...

  10. C#4.0图解教程 - 第24章 反射和特性 – 2.特性

    1.特性 定义 Attribute用来对类.属性.方法等标注额外的信息,贴一个标签(附着物) 通俗:给 类 或 类成员 贴一个标签,就像航空部为你的行李贴一个标签一样 注意,特性 是 类 和 类的成员 ...