Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Express学习笔记

28
May
2016

Express学习笔记

By Alex
/ in JavaScript
/ tags 学习笔记
0 Comments
简介

Express是一个基于Node.js的快速、简洁的Web开发框架。它提供了大量的HTTP助手方法、中间件供你使用,很大程度上减轻了开发的工作量。

Express在Node.js内置的网络模块的基础上封装了一个薄的层,在此层中提供Web应用后端的基础功能,你仍然可以使用Node.js的集群功能。

安装

在通过npm初始化当前包时,Express建议将入口点名称app.js,执行下面的命令把Express添加为当前工程的依赖:

Shell
1
npm install express --save
Hello World
JavaScript
1
2
3
4
5
6
7
8
9
10
11
var express = require( 'express' )
var app = express()
 
// req、resp和Node.js的http模块的回调参数一样
app.get( '/', function ( req, resp ) {
    resp.send( 'Hello World!' )
} )
 
app.listen( 3000, function () {
    console.log( 'Example app listening on port 3000!' )
} )

上述代码在3000端口启动一个HTTP服务器,并且对路由 / 响应一个简短的字符串。当你访问其它路径时,Express会响应404代码。

应用生成器

Express提供了一个模块,用于快速生成Express应用的结构和脚手架代码,执行下面的命令安装此模块:

Shell
1
npm install express-generator -g

该模块提供了一个express命令,其格式为:

Shell
1
express [options] [dir]
常用选项
选项 说明
-v, --view 指定使用的视图层引擎,支持ejs|hbs|hjs|jade|pug|twig|vas,默认jade
-c, --css 指定使用的CSS引擎,支持less|stylus|compass|sass,默认使用基本的CSS格式
-f, --force 允许针对非空目录执行命令
--git 添加.gitignore
命令效果

我们可以执行下面的命令生成一个Express应用程序:

Shell
1
express --view=jade --css=sass express-study

执行命令: DEBUG=express-study:* npm start  可以启动Web应用。

命令会生成如下目录结构:

子目录 说明
bin 包含服务器启动脚本
public 静态资产,例如图片、客户端JS、样式表
routes 路由定义
views 视图模板
静态文件

 要想处理类似图片、CSS、客户端脚本之类的静态文件,可以使用 express.static 这个内置的中间件。示例如下:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
// root 静态资产在文件系统中的根目录
// options,选项,一个对象
express.static(root, [options])
 
// 设置public文静态目录
app.use(express.static('public'))
// 可以调用多次
app.use(express.static('files'))
 
// URL可以映射到不同的目录
app.use('/static', express.static('public'))
选项

构造express.static中间件实例时,可以提供以下选项:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var options = {
    // 点号开头的文件如何处理。allow、deny(401)、ignore(404)
    dotfiles: 'ignore',
    // 添加etag头,默认true
    etag: false,
    // 默认扩展名
    extensions: [ 'htm', 'html' ],
    // 默认情况下,访问目录时自动返回其下的index.html
    index: false,
    // 以毫秒计的 max-age 头
    maxAge: '1d',
    // 是否路径名对应了目录,是否重定向以 / 结尾的URL
    redirect: false,
    // 设置响应头
    setHeaders: function ( res, path, stat ) {
        res.set( 'x-timestamp', Date.now() )
    }
}
 
app.use( express.static( 'public', options ) ) 
路由
路由方法

所谓路由,是指决定应用程序如何相应客户端针对某个特定端点(Endpoint)的请求的过程。端点是URI和HTTP方法的组合。每个路由可以拥有多个处理函数,这些处理函数在路由匹配时执行。

要定义一个路由,可以调用:

JavaScript
1
2
3
4
5
app.METHOD(PATH, HANDLER)
// app 为Express实例,也可以在express.Router对象上调用
// METHOD 是小写的HTTP方法名称
// PATH 服务器路径,不需要协议、域名、端口部分
// HANDLER 处理函数

示例:

JavaScript
1
2
3
4
5
6
7
app.get('/', function (req, res) {
    res.send('Hello World!')
})
 
app.delete('/user', function (req, res) {
    res.send('Got a DELETE request at /user')
})

Express支持以下HTTP方法:get, post, put, head, delete, options, trace, copy, lock, mkcol, move, purge, propfind, proppatch, unlock, report, mkactivity, checkout, merge, m-search, notify, subscribe, unsubscribe, patch, search, connect,这些方法都对应到Express的实例方法。

有一个特殊的路由方法all,用于为某个路径上的所有HTTP方法加载中间件函数:

JavaScript
1
2
3
4
app.all( '/secret', function ( req, res, next ) {
    console.log( 'Accessing the secret section ...' )
    next() // 把控制权交给下一个处理器
} );
路由路径

路由路径即端点的URI部分,可以包含字符串、字符串模式、正则式。协议、域名、端口、查询串,不属于路由路径的组成部分。

对于字符串模式,可以使用 ? + * () :

JavaScript
1
2
3
4
'/ab?cd'      // 重复0-1次,匹配 /abcd  /acd
'/ab+cd'      // 重复1-N次,匹配 /abcd  /abbcd    /abbbcd
'/ab*cd'      // 匹配任意个字符,匹配 /abcd  /abANYcd
'/ab(cd)?e'   // 匹配 /abe  /abcde

基于字符串来匹配路径时, -  . 按照字面值解析。

使用正则式的例子:

JavaScript
1
2
3
4
5
6
// 匹配路径中具有字符a的任何路径
app.get( /a/, function ( req, res ) {
} )
// 匹配以fly结尾的任何路径
app.get( /.*fly$/, function ( req, res ) {
} )
路由参数

URI中片断中可以包含变量声明,捕获的变量存放到 req.params 对象中。例如:

1
2
3
/users/:userId/books/:bookId
# 可以捕获到 这样的参数:
{ "userId": "34", "bookId": "8989" }

由于 -  . 按照字面值解析,因此可以作为路由参数之间的分隔符:

1
2
3
/flights/:from-:to
# 可以捕获到 这样的参数:
{ "from": "LAX", "to": "SFO" }
路由处理器

你可以注册多个行为类似于中间件的路由处理器,用于处理请求。需要注意的是,这些回调函数可以调用 next() 来把请求处理权转交给下一个路由处理器:

JavaScript
1
2
3
4
5
6
router.get( '/', function ( req, res, next ) {
    next();  // 请求由下面的那个路由处理器来处理
}, function ( req, res ) {
    res.send( 'Hello World' );
    res.end();
} );

多个路由器可以顺序传入,或者形成数组一起传入,或者两种方式混用。

app.route()

该方法用于创建链式的路由处理器:

JavaScript
1
2
3
4
5
6
7
8
9
10
app.route( '/book' )
   .get( function ( req, res ) {
       res.send( 'Get a random book' )
   } )
   .post( function ( req, res ) {
       res.send( 'Add a book' )
   } )
   .put( function ( req, res ) {
       res.send( 'Update the book' )
   } );
express.Router

该类用于创建模块化的、可挂载的路由处理器。Router的实例是一个完整的中间件和路由子系统。

下面的代码创建一个Router,在其中加载了一个中间件函数,然后定义几个路由规则(route):

routes/birds.js
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var express = require( 'express' )
// 创建一个Router
var router = express.Router()
 
// 加载一个仅针对次Router使用的中间件
router.use( function timeLog( req, res, next ) {
    console.log( 'Time: ', Date.now() )
    next()
} )
 
// 定义index路由规则
router.get( '/', function ( req, res ) {
    res.send( 'Birds home page' )
} )
// 定义about路由规则
router.get( '/about', function ( req, res ) {
    res.send( 'About birds' )
} )
 
// 必须导出Router对象
module.exports = router

你需要在Express应用中加载上述Router模块:

JavaScript
1
2
3
var birds = require('../routes/birds')
// Router属于中间件。把URL前缀交由该Router处理
app.use('/birds', birds)

这样,你访问/birds、/birds/about这两个URL时, routes/birds.js中定义的路由将获得处理权。 

请求和响应
响应对象

下面的方法用于像客户端发送响应,并且终结请求-响应处理周期。如果这些方法中的任一个都没有被调用,则客户端一直被挂起: 

方法 说明
res.download() 提示一个文件加载
res.end() 结束响应处理
res.json() 发送一个JSON响应
res.jsonp() 发送一个带有JSONP支持的JSON响应
res.redirect() 发送一个重定向响应
res.render() 渲染一个视图模板
res.send() 发送某种类型的响应
res.sendFile() 以字节流发送文件
res.sendStatus() 发送一个响应状态码,并将其字符串描述作为响应体发送
中间件

Express是一个路由、中间件Web框架,其本身的功能很少。一个典型的Express应用实际上是一系列中间件函数调用的组合。

Express的中间件与React的中间件的运作方式很类似。所谓Express中间件,是指以req、res、next为参数的函数。这三个函数分别代表请求、响应、应用程序请求处理周期中的下一个中间件函数。中间件可以具有以下行为:

  1. 执行任意代码
  2. 对请求、响应对象进行操作和修改
  3. 终止请求处理周期
  4. 调用下一个中间件

如果当前中间件没有终止请求处理周期,那么它就必须调用下一个中间件来移交控制权,否则客户端会一直挂起。

传递给Express或者express.Router的get/post...函数的路由处理器,本质上都属于中间件。

使用中间件

要加载一个中间件,可以调用Express或者express.Router的 use() 方法。下面的代码,在路由规则之前加载一个中间件:

JavaScript
1
2
3
4
5
6
7
8
9
10
var myLogger = function ( req, res, next ) {
    console.log( 'LOGGED' )
    next()
}
 
app.use( myLogger )
 
app.get( '/', function ( req, res ) {
    res.send( 'Hello World!' )
} )

中间件的执行顺序,与它的加载顺序一致。先加载的中间件会先执行。 如果上面的use在get之后调用,则不会打印日志,因为get指定的路由处理器已经终止了请求处理周期。

中间件分类

中间件依据作用域、用途的不同,可以分为:应用级中间件、Router级中间件、错误处理中间件。对于应用级、Router级中间件,可以在加载中间件时指定一个可选的挂载路径(mount path)。你也可以把一系列中间件同时挂载,从而创建中间件系统的一个子栈(Sub stack)。

应用级中间件

要绑定应用级中间件,需要调用app.use()或者app.METHOD(),后者用于注册特殊用途的中间件——路由处理器,METHOD必须是某种HTTP方法的小写形式。路由处理器也可以通过use注册,这样它就可以对所有类型的请求生效:

JavaScript
1
2
3
4
5
6
// 针对 /user/xxx 所有类型的HTTP方法生效
app.use( '/user/:id', function ( req, res, next ) {
} )
// 针对 /user/xxx 的GET方法生效
app.get( '/user/:id', function ( req, res, next ) {
} )

下面的例子,同时在一个挂载点加载多个中间件,形成子栈:

JavaScript
1
2
3
4
5
app.use( '/user/:id', function ( req, res, next ) {
    next()
}, function ( req, res, next ) {
    next()
} )

调用 next('route')  可以跳过当前子栈。你只能在app.METHOD()或者router.METHOD()中调用该方法:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 子栈一
app.get( '/user/:id', function ( req, res, next ) {
    // 如果用户ID为0,则跳过当前子栈,执行下一个路由规则(子栈二)
    if ( req.params.id === '0' ) next( 'route' )
    // 否则,执行当前子栈的下一个中间件
    else next()
}, function ( req, res, next ) {
    // 子栈一的第二个中间件
    res.render( 'regular' )
} )
  
// 子栈二
app.get( '/user/:id', function ( req, res, next ) {
    res.render( 'special' )
} )
Router级中间件

与应用级的中间件工作方式一致,只是绑定到一个express.Router实例。express.Router用于处理URI的某个特定命名空间(前缀)。

错误处理中间件

这种中间件的签名中多一个参数:

JavaScript
1
2
3
4
app.use( function ( err, req, res, next ) {
    console.error( err.stack )
    res.status( 500 ).send( 'Something broke!' )
} )

通常这种中间件放在链条的尾部,也就是应该最后调用。 

如果调用next()时指定任何不为'route'的其它参数,Express认为错误已经发生,后续所有非错误处理中间件将被跳过。

Express包含了一个内置的默认错误处理器中间件,该中间件会自动的被加到中间件链条的尾部。如果你调用next(err)而又不提供任何错误处理中间件,则次默认中间件会处理err。

使用中间件

从Express 4.x开始,除了 express.static 以外的中间件函数都被分离到单独的模块中。

你可以使用第三方中间件来扩充Express的功能。例如:

Shell
1
2
# Cookie解析器中间件
npm install cookie-parser --save

JavaScript
1
2
3
4
5
6
var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')
 
// 加载中间件
app.use(cookieParser())
模板引擎

通过模板引擎,你可以在应用程序中编写静态的模板文件,并在运行时替换模板中的变量占位符,最终生成客户端可用的HTML文件。

Express支持Pug、Mustache、EJS等主流模板引擎。它默认使用Jade引擎。

设置

要使用模板引擎,你需要调用app.set来设置应用程序属性:

属性 说明
views 设置模板文件的存放目录,默认值是应用根目录下的view子目录。示例:
JavaScript
1
app.set( 'views', './views' )
view engine 设置使用的模板引擎。示例:
JavaScript
1
app.set( 'view engine', 'jade' )
渲染模板

调用res.render()方法可以渲染模板,并将其写入到响应体中:

JavaScript
1
2
3
4
app.get( '/', function ( req, res ) {
    // 渲染views/index.jade,并写入响应
    res.render( 'index', { title: 'Hey', message: 'Hello there!' } )
} )
Jade

这是Express默认的模板引擎,你可以单独使用它:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var jade = require( 'jade' );
var options = {
    compileDebug: false,// 是否包含调试信息
    pretty: true // 格式化结果
}
// 编译模板为函数
var fn = jade.compile( 'string of jade', options );
var html = fn( locals );
 
// 渲染字符串中的模板
var html = jade.render( 'string of jade', merge( options, locals ) );
 
// 渲染文件中的模板
var html = jade.renderFile( 'filename.jade', merge( options, locals ) );

 模板示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
doctype html
// 元素和属性,属性值可以包含插值
html(lang="#{lang}")
    // 嵌套的元素
    head
        // 给元素赋值,即设置内部其文本节点,可以使用JS表达式
        title= pageTitle
        // 多个属性,用空格分开,属性值也可以使用JS表达式
        script(type=scrType src='').
            if ( foo ) bar( 1 + 5 )
    body
        h1 Jade - node template engine
        // div,id为container,样式类为col
        #container.col
            // 条件控制
            if youAreUsingJade
                // 插值
                p #{name} are amazing
            else
                p Get on it!
调试

Express使用debug模块来记录路由匹配、启用的中间件函数、应用程序模式、请求处理流程等信息。

debug是一个类似于console.log()的模块,但是在产品发布中,你不需要注释掉调试代码。因为除非设置好环境变量DEBUG,它不会做日志记录工作。

要查看所有Express的内部日志,可以设置: DEBUG=express:* 。要查看当前应用的日志信息,可以设置: DEBUG=express-study 。要启用指定名字空间的调试信息,可以设置: DEBUG=http,mail,express:* 

访问数据库

要在Express应用中访问数据库,你需要加载合适的Node.js数据库驱动程序。Node.js支持主流的数据库,下面举例说明。

MySQL
安装驱动
Shell
1
npm install mysql --save 
代码示例
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var mysql = require( 'mysql' )
// 创建连接
var connection = mysql.createConnection( {
    host: 'localhost',
    user: 'dbuser',
    password: 's3kreee7'
} )
connection.connect()
// 查询,注意Node.js这种强制的异步风格
connection.query( 'SELECT 1 + 1 AS solution', function ( err, rows, fields ) {
    if ( err ) throw err
 
    console.log( 'The solution is: ', rows[ 0 ].solution )
} )
// 关闭连接
connection.end()
MongoDB
安装驱动
Shell
1
npm install mongodb --save 
代码示例
JavaScript
1
2
3
4
5
6
7
8
9
10
var MongoClient = require( 'mongodb' ).MongoClient
 
MongoClient.connect( 'mongodb://localhost:27017/animals', function ( err, db ) {
    if ( err ) throw err
 
    db.collection( 'mammals' ).find().toArray( function ( err, result ) {
        if ( err ) throw err
        console.log( result )
    } )
} )
Redis 
安装驱动
Shell
1
npm install redis --save 
代码示例
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var client = require( 'redis' ).createClient()
 
client.on( 'error', function ( err ) {
    console.log( 'Error ' + err )
} )
 
client.set( 'string key', 'string val', redis.print )
client.hset( 'hash key', 'hashtest 1', 'some value', redis.print )
client.hset( [ 'hash key', 'hashtest 2', 'some other value' ], redis.print )
 
client.hkeys( 'hash key', function ( err, replies ) {
    console.log( replies.length + ' replies:' )
 
    replies.forEach( function ( reply, i ) {
        console.log( '    ' + i + ': ' + reply )
    } )
 
    client.quit()
} )
进程管理器

在生产环境使用Express时,你可以使用进程管理器,来达成以下目标:

  1. 在应用程序崩溃后,自动重新启动它
  2. 获取运行性能、资源消耗信息
  3. 动态调整设置以提高性能
  4. 控制集群

进程管理器是应用程序的容器,使用它可以便利部署、提供高可用性、允许你在运行时管理应用程序。有时候它就像服务器软件那样。

Express或者其它Node.js最常使用的进程管理器软件包括StrongLoop Process Manager、PM2、Forever。

StrongLoop PM

StrongLoop是一个产品级的Node.js进程管理器,它包含了内置的负载均衡、监控、多主机部署支持,它还提供图形化的控制台。使用StrongLoop你可以:

  1. 构建、打包、部署Node.js应用程序,并在本地、远程机器上部署
  2. 显示CPU、内存剖析信息,以便性能优化和诊断内存泄漏
  3. 确保进程、集群不宕机
  4. 显示应用程序的性能统计信息
  5. 轻松的管理多主机部署、Nginx集成

StrongLoop提供的命令行接口是 slc,图形化接口是Arc。

安装和使用

执行下面的命令安装:

Shell
1
npm install -g strongloop

命令行示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
cd express-study
# 启动Strongloop
slc start
# 查看状态和已经部署的应用
slc ctl
# 停止、重启、软重启
# 软重启给予工作进程一段时间,来关闭既有连接
slc ctl stop express-study
slc ctl restart express-study
slc ctl soft-restart express-study
# 移除受管程序
slc ctl remove
安全性最佳实践
使用TLS

使用HTTPS可以确保客户端 - 服务器之间的连接是安全的,数据不会被篡改或者窃听。

使用Helmet

通过设置合适的HTTP头,该中间件能够保护你的应用不受场景的Web弱点的影响。Helmet会设置以下头:

Content-Security-Policy 防止跨站脚本攻击,或者其它跨站攻击
X-Powered-By 移除该头
Public Key Pinning 防止基于伪造证书的中间人攻击
Strict-Transport-Security 强制使用HTTPS协议
X-Download-Options 设置为noopen,禁止IE8+,防止用户下载程序并执行
Cache-Control  Pragma 禁止客户端缓存
X-Content-Type-Options 防止浏览器嗅探MEME类型
X-Frame-Options 提供Clickjacking保护
X-XSS-Protection 启用跨站脚本过滤,较新浏览器支持

执行下面的命令安装:

Shell
1
npm install --save helmet

使用:

JavaScript
1
2
var helmet = require( 'helmet' )
app.use( helmet() )

如果你不使用Helmet,至少要禁用以下响应头:

JavaScript
1
app.disable('x-powered-by')

该头允许攻击者探测服务器类型。攻击者可能针对Express进行攻击。  

Cookie安全

Express有两个主要中间件,实现基于Cookie的Session:express-session、cookie-session。两者的主要区别是存储Session数据的方式:

  1. express-session:把Session数据存放在服务器上,客户端仅仅存储一个Session ID。该中间件默认在内存中存储Session数据,因此不适用产品环境。注意选择合适的Session存储
  2. cookie-session:串行化整个Session数据到客户端,注意两点,一个是4096字节的限制,第二个是这些数据可能被客户端的恶意软件读取
不要使用默认Cookie名称

这会让你容易受到攻击,修改示例:

JavaScript
1
2
3
4
5
var session = require( 'express-session' )
app.use( session( {
    secret: 's3Cur3',
    name: 'sessionId'
} ) )
设置Cookie安全选项
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ar session = require( 'cookie-session' )
var express = require( 'express' )
var app = express()
 
var expiryDate = new Date( Date.now() + 60 * 60 * 1000 )
app.use( session( {
    name: 'session',
    keys: [ 'key1', 'key2' ],
    cookie: {
        secure: true, // 确保浏览器仅仅通过HTTPS发送Cookie
        httpOnly: true,  // 确保仅仅通过HTTP/HTTPS发送Cookie,禁止客户端JS发送,防止跨站脚本攻击
        domain: 'example.com', // 最小化域名
        path: 'foo/bar',  // 最小化匹配路径
        expires: expiryDate // 最小化有效期
    }
} ) )
其它安全
安全风险 建议
暴力密码破解 使用StrongLoop API Gateway可以限制请求的频率,或者使用express-limiter这样的中间件
跨站请求伪造CSRF 使用csurf中间件
跨站脚本攻击 注意总是过滤、处理用户输入
SQL注入攻击 可以使用sqlmap来检查应用程序弱点
性能最佳实践 
使用Gzip压缩

Gzip压缩可以大大减小响应体的大小,因而可以加快速度、节约带宽。使用中间件:

JavaScript
1
2
3
4
var compression = require( 'compression' )
var express = require( 'express' )
var app = express()
app.use( compression() )

对于高流量的网站,最好在反向代理服务器中实现压缩,此时不需要使用Express中间件。Nginx是反向代理的优秀备选,可以启用它的Gzip支持。 

不要同步调用

使用同步调用将极大的影响Node.js应用处理并发请求的能力,Node.js选项 --trace-sync-io 可以监控同步I/O并发起警告。

正确的日志记录

在应用程序中记录日志的目的由两个:调试、记录程序活动。

使用 console 对象的方法是最基本的日志记录手段,但是这些方法都是同步的。如果标准输出是一个Terminal或者文件,应用性能会受到很大影响。

对于调试日志,可以结合debug模块和DEBUG环境变量,这样在产品环境下不会有任何影响。

对于记录程序活动的日志,可以使用Winston或者Bunyan这样的日志库。

正确的处理异常

如果遭遇未捕获(uncaught exception)的异常,Node.js应用程序会崩溃掉。作为最后保险手段,你需要确保Worker进程崩溃后能自动重启。

重启总是要消耗一定的时间的,尽管Node.js应用通常启动很快。因此,你应该在异常发生的第一现场就做好处理。

不要捕获uncaughtException

全局的异常处理器,用来捕获JS代码中冒泡到事件循环的异常。这通常不是好办法,虽然程序会继续运行,但是其状态可能已经被破坏。

try-catch

在同步代码中使用这种构造:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
app.get('/search', function (req, res) {
    setImmediate(function () {
        var jsonStr = req.query.params
        try {
            var jsonObj = JSON.parse(jsonStr)
            res.send('Success')
        } catch (e) {
            res.status(400).send('Invalid JSON string')
        }
    })
})
promises

使用Promise可以处理异步代码中的任何异常,你只需要在Promise链的尾部添加catch()即可:

JavaScript
1
2
3
4
5
6
7
8
9
10
11
app.get( '/', function ( req, res, next ) {
    queryDb()
        .then( function ( data ) {
        } )
        .then( function ( csv ) {
        } )
        .catch( next )
} )
app.use(function (err, req, res, next) {
    // 在这里处理异常
})

但是注意:

  1. 异步代码必须返回Promise,如果某个库不返回Promise,则你需要使用助手函数,例如 Bluebird.promisifyAll()  将其转换为Promise
  2. EventEmitter仍然可能导致未捕获异常,你必须合理的处理error事件:
    JavaScript
    1
    2
    3
    let stream = getLogoStreamById( company.id )
    // 转交给错误处理中间件
    stream.on( 'error', next ).pipe( res )
配置好运行环境
NODE_ENV

设置该环境变量为production,则Express会:

  1. 缓存视图模板
  2. 缓存Sass等CSS扩展生成的CSS文件
  3. 生成较为简洁的错误信息 

 如果你希望编写运行环境依赖的代码,可以访问 process.env.NODE_ENV 。

自动重启

使用StrongLoop来管理应用程序进程,并把StrongLoop安装为系统服务:

Shell
1
2
# 安装StrongLoop为系统服务,系统启动后,它会自动启动受管的应用程序
sudo sl-pm-install --systemd
集群

多核系统中,通常会启动与核心数相同的Node.js应用,以提高性能。你可以使用Node.js内置的cluster模块或者StrongLoop。

请求缓存

使用Varnish或者Nginx 之类的缓存服务器,避免针对同一请求反复的执行Node.js代码。

负载均衡

考虑使用Nginx或者HAProxy之类的负载均衡器。要实现会话关联性(session affinity),可以使用Redis之类的外部存储来保存会话数据。

StrongLoop集成了一个Nginx控制器,可以方便的在多台机器上部署Node.js应用并实现负载均衡。

← Lua学习笔记
Next Post →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 使用Babel进行JS转码
  • ExtJS 4的容器机制
  • 浅析ExtJS新特性
  • 基于Chrome Developer Tools的JavaScript开发与调试
  • 基于AngularJS开发Web应用

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • Bazel学习笔记 38 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
  • Three.js学习笔记 24 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2