本篇主要介绍如何提高koa web系统的稳定性,能够尽量多得捕捉&处理系统异常,减少系统崩溃的几率。
本篇将从一下几个角度介绍如何捕获/处理系统异常一、回调函数, 返回异常
异步调用中回调函数里的异常无法被外部捕获,因此在一个函数 /API 内部发送异常是,将异常作为地一个参数传递给回调函数,例如: readFile这个函数:
12345 fs.readFile(filePath,(err, data) => { //回调函数(第一个参数为异常, 第二个为输出值)if(err) console.log('failed to read file')else console.log('success to read file')})如何编写如readFile这样的API, 下面以 建立 redis 链接为例:
a、 在 ioredis 中, 在 node 与 redis 建立连接的时候,ioredis会输出一个 node 事件 ‘connect’, 此时回调函数返回 连接对象;
b、如果在链接过程中出现异常,则 ioredis 输出事件 ‘error’,此时回调函数返回异常:
12345678910111213 let clientCreate = (config, callback_) => {let redis = new ioredis(config) //链接请求redis.on('connect', () => {callback_(null, redis) //链接成功})redis.on('error', (err) => {callback_(err, null) //捕捉异常})}clientCreate(config, (err, conn) => {if(err) console.log('connect failed')else console.log('connect successfuly')})通过回调返回异常的方法,可以把相关异常锁在一个函数中, 而不是放在两个函数(node 事件)中,这样函数使用时,可以有序处理异常。
二、 try/catch 异常捕获
1、try/catch 这个是最容易想到的,但是这个方法在回调/异步函数的满天飞的 nodejs 这边几乎无用武之地, 例如:
1234567 try {setTimeout(function() {throw new Error('test error')}, 5)}catch(err) {alert('err', err)}上面这个例子抛出的一场,就无法被 catch捕获。 如何捕获回调/异步函数产生的异常?
2、co+Generator函数中使用try/catch,这个在之前的篇幅中已经有所涉及例如:
1234567891011 route.get('/api/user', co.wrap(function* (ctx, next) {try {let users = JSON.parse(yield readFromFile()) //readFromFile为异步Promise函数logger.debug('users', users)ctx.body = JSON.stringify({status: 'success', data: users})} catch(err) {logger.error('err', err)ctx.status = 500ctx.body = JSON.stringify({status: 'failed'})}}))在 co 包裹的 generator 函数中, 由于 需要执行完 yield 那一步才会继续执行下一部,因此,抛出的异常可以被 try/catch 捕获。
三、使用Promise对象
co+Generator 其实使用的也是Promise对象, 原理是将异步函数封装成 Promise 对象, co 源码。Promie 对象使用方式如下所示:
1234567891011121314 //用promise 封装异步函数let readFromFile = (filePath) => {return new Promise((resolve, reject) => {fs.readFile(filePath, (err, data) => {if(err) {reject(err) //抛出异常}resolve(data) //输出结果})})}//执行promise函数readFromFile('./user.json').then(result => {console.log('result', result)}).catch(err => { console.log('err', err)})在 promise 对象中出现异常,可以使用 reject 函数把 Promise 对象变为失败,在 Promise 对象执行时,能够通过 catch() 方法捕获异常
四、gracefu+cluster方案
引入依赖 graceful, npm install –save graceful
原理:graceful: 如果系统有uncaughtException(即未被捕获的异常), 延迟一段时间后退出进程
cluster: 实现多进程,如果有进程退出,自动重启1、 cluster部分实现: 新建文件 dispatch.js
1234567891011121314 const cluster = require('cluster')const path = require('path')cluster.setupMaster({ //设置线程参数exec: path.join(__dirname, 'worker.js') // ./worker.js 为 graceful 部分})cluster.fork() //创建一个进程cluster.on('disconnect', function(worker) { //如果线程断开let w = cluster.fork() //重新新起一个线程,这里是gracefu+cluster方案核心之一console.error('[%s] [master:%s] wroker:%s disconnect! new worker:%s fork', new Date(), process.pid, worker.process.pid, w.process.pid)})cluster.on('exit', function(worker) { //线程退出事件console.error('[%s] [master:%s] wroker:%s exit!',new Date(), process.pid, worker.process.pid)})2、 graceful部分实现: 新建文件 worker.js
12345678910111213 const graceful = require('graceful') //引入 gracefulconst app = require('./app.js') // koa web 应用graceful({server: [app], //监听 koa web运行状态killTimeout: 3000, //延时 3000mserror: function (err, throwErrorCount) { //异常if(err.message) {err.message += ' (uncaughtException throw ' + throwErrorCount +' times on pid:' + process.pid + ')'console.log(err.message)}}})3、 启动时通过 dispatch.js ==> node –harmony dispatch.js, 如果系统有未捕获的异常,3秒后会自动重启
五、PM2
gracefu+cluster 这套方案在配置层面相对复杂, 可以使用更简单的 PM2 模块启动监控 node 服务,他可以自动利用 多核cpu, 监控线程等,功能非常强大。
PM2 github
PM2 全局安装: npm install pm2 -g
PM2 启动服务: pm2 start app.js
PM2 管理的进程: pm2 list
更多的命令可以参看: pm2 github。more
对于node 来说还有一个 domain 模块可以捕获(uncaughtException)异常, 但是这个模块相对不怎么好用,使用 gracefu+cluster 或 PM2 可以解决相应的问题。