express-16 与生产相关的问题2
处理未捕获的异常
- 在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的代理服务器,比如proxy和node-http-proxy。
如果要求不高,或者是用于开发,这些都是很好的选择。对于生产环境而言,我推荐用Nginx或HAProxy (这两个都是免费的,尽管提供服务是收费的)。
如果确实配置了一台代理服务器,请确保告知Express你用了代理,并且它应该得到信任:
app.enable('trust proxy');
- 这样可以确保
req.ip
、req.protocol
和req.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的更多相关文章
- express-15 与生产相关的问题
执行环境 Express支持执行环境的概念,它是一种在生产.开发或测试模式中运行应用程序的方法.实际上你可以按自己的想法创建很多种不同的环境. 要记住,开发.生产和测试是"标准"环 ...
- Ubuntu 16.04 samba相关配置
samba是 SMB/CIFS网络协议的重新实现,它作为NFS的补充使得在Linux和Windows系统之间进行文件共享.打印更容易实现. 相关介绍: SAMBA套件: (1)samba:这个套件主要 ...
- ubuntu 16.04 mysql 相关
如何彻底卸载某一版本的数据库 彻底删除ubuntu下的mysql: 1.删除mysql的数据文件 sudo rm /var/lib/mysql/ -R 2.删除mqsql的配置文件 sudo rm / ...
- MySQL全面瓦解16:存储过程相关
概述 大多数SQL语句都是针对一个或多个表的单条语句.但并非所有业务都这么简单,经常会有复杂的操作需要多条语句才能完成. 比如用户购买一个商品,要删减库存表,要生成订单数据,要保存支付信息等等,他是一 ...
- Ubuntu 16.10 server 相关
1)安装图形化界面 sudo apt-get install xinit sudo apt-get install gnome 2)启用root账号 ① sudo passwd root ② 修改/e ...
- 16.和input相关的知识点
1.改变input里面placeholder颜色 <input class="pre_name" type="text" placeholder=&quo ...
- SpringBoot中使用mybatis-generator自动生产
步骤: 1.在pom.xml中添加插件配置 <plugin> <groupId>org.mybatis.generator</groupId> <artifa ...
- Express使用手记:核心入门
入门简介 Express是基于nodejs的web开发框架.优点是易上手.高性能.扩展性强. 易上手:nodejs最初就是为了开发高性能web服务器而被设计出来的,然而相对底层的API会让不少新手望而 ...
- 使用express, create-react-app, mongodb搭建react模拟数据开发环境
提要 最近刚刚完成了一个vue的项目,其中涉及的用户数有6000多个以及其他数据也比较多,为了在前端能够真实的进行数据模拟,所有把全量数据拷贝下来放到了api.json中.这样导致整个api.json ...
随机推荐
- C#有关日期的使用方法
DateTime dt = DateTime.Now; //当前时间 DateTime startWeek = dt.AddDays( - Convert.ToInt32(dt.DayOfWeek.T ...
- 【转】深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
一.可能的组合: (1)const char*p (2)char const*p (3)char *const p(4)const char **p (5)char const**p (6)char ...
- Index on DB2 for z/OS: DB2 for z/OS 的索引
可以创建在任何表上的索引: Unique Index:An index that ensures that the value in a particular column or set of col ...
- ARPPING
http://www.tuicool.com/articles/M7B3umj http://lixcto.blog.51cto.com/4834175/1571838/
- ASP.NET Web Api 实现数据的分页(转载)
转载地址:http://www.cnblogs.com/fzrain/p/3542608.html 前言 这篇文章我们将使用不同的方式实现手动分页(关于高端大气上档次的OData本文暂不涉及,但有可能 ...
- Pyqt 音视频播放器
在寻找如何使用Pyqt做一个播放器时首先找到的是openCV2 openCV2 貌似太强大了,各种关于图像处理的事情它都能完成,如 读取摄像头.图像识别.人脸识别. 图像灰度处理 . 播放视频等,强 ...
- 【转载】 python修饰符@
@符号在python语言中具有特殊含义,用来作为修饰符使用, @修饰符有点像函数指针,python解释器发现执行的时候如果碰到@修饰的函数,首先就解析它,找到它对应的函数进行调用,并且会把@修饰下面一 ...
- hdu 2203:亲和串(水题,串的练习)
亲和串 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...
- Go 中的反射要点
简介 反射是元数据编程的一种形式,指的是程序获得本身结构的一种能力.不同语言的反射模型实现不一样,本文中的反射,仅仅指的是Go语言中的反射模型. 类型以及接口 这个基本概念需要清晰,这里不详细展开. ...
- Windows环境下Oracle数据库的自动备份脚本
批处理文件(.bat) @echo off echo ================================================ echo Windows环境下Oracle数据 ...