媒介
这将是一个分为两部份,内容是关于在临盆环境下,跑Express
运用的最好实践。第一部份会关注安全性,第二部份则会关注机能和可靠性。当你读这篇文章时,会假定你已对Node.js
和web开辟有所相识,而且对临盆环境有了观点。
关于第一部份,请参阅Express在临盆环境下的最好实践 – 安全性。
概览
正如第一部份所说,临盆环境是供你的最终用户们所运用的,而开辟环境则是供你开辟和测试代码所用。故关于和两个环境的要求,是非常差别的。比方,在开辟环境下,你没必要斟酌伸缩性和可靠性另有机能的题目,但这些在临盆环境下都非常主要。
接下来,我们会将此文分为两大部份:
须要对代码做的事,即开辟部份。
须要对环境做的事,即运维部份,
须要对代码做的事
为了提拔你运用的机能,你能够经由过程:
运用
gzip
紧缩制止运用同步要领
运用中间件来供应静态文件
适当地打印日记
合理地处置惩罚非常
运用gzip
紧缩
Gzip
紧缩能够显著地削减你web运用的响应体大小,从而提拔你的web运用的响应速度。在Express
中,你能够运用compression中间件来启用gzip
:
var compression = require('compression');
var express = require('express');
var app = express();
app.use(compression());
关于在临盆环境中,流量非常大的网站,最好是在反向代办层处置惩罚紧缩。假如如许做,那末就不就须要运用compression
了,而是须要参阅Nginx
的ngx_http_gzip_module
模块的文档。
制止运用同步要领
同步要领会在它返回之前都一向壅塞线程。一次零丁的挪用能够影响不大,但在流量非常庞大的临盆环境中,它是不可接收的,能够会致使严峻的机能题目。
虽然大多数的Node.js
和其第三方库都同时供应了一个要领的同步和异步版本,但在临盆环境下,请老是运用它的异步版本。唯一能够破例的场景多是,假如这个要领只在运用初始化时挪用一次,那末运用它的同步版本也是能够接收的。
假如你运用的是Node.js
4.0+ 或 io.js
2.1.0+ ,你能够在启动运用时附上--trace-sync-io
参数来搜检你的运用中那里运用了同步API。更多关于这个参数的信息,你能够参阅io.js
2.1.0的更新日记。
运用中间件来供应静态文件
在开辟环境下,你能够运用res.sendFile()
来供应静态文件。但在临盆环境下,这是不被许可的,因为这个要领会在每次要求时都邑对文件体系举行读取。res.sendFile()
并非经由过程体系要领sendfile
完成的。
对应的,你能够运用serve-static中间件来为你的Express
运用供应静态文件。
更好的挑选则是在反向代办层上供应静态文件。
适当地打印日记
总得来说,为你的运用打印日记的目的有两个:调试和操纵纪录。在开辟环境下,我们一般运用console.log()
或console.err()
来做这些事。然则,当这些要领的输出目的是终端或文件时,它们是同步的,所以它们并不适用于临盆环境,除非你将输出导流至另一个顺序中。
为了调试
假如你正在为了调试而打印日记。那末你能够运用一些专用于调试的库如debug,用于替换console.log()
。这个库能够经由过程设置DEBUG
环境变量来掌握仔细哪些信息会被打印。虽然这些要领也是同步的,但你肯定不会在临盆环境下举行调试吧?
为了操纵纪录
假如你正在为了纪录运用的运动而打印日记。那末你能够运用一些日记库如winston或Bunyan,来替换console.log()
。更多关于这两个库的概况,能够参阅这里。
合理地处置惩罚非常
Node.js
在碰到未处置惩罚的非常时就会退出。假如没有合理地捕捉并处置惩罚非常,这会使你的运用崩溃和离线。假如你运用了一个自动重启的东西,那末你的运用则会在崩溃后马上重启,而且荣幸的是,Express
运用的重启时候一般都很快。然则不管怎样,你都想要只管防止这类崩溃。
为了保证你合理处置惩罚非常,请顺从以下指导:
运用
try-catch
运用
promise
不应当做的事
你不应当监听全局事宜uncaughtException
。监听该事宜将会致使历程碰到未处置惩罚非常时的行动被转变:历程将会疏忽此非常并继承运转。这听上去很好,然则假如你的运用中存在未处置惩罚非常,继承运转它是非常风险的,因为运用的状况最先变得不可展望。
所以,监听uncaughtException
并非一个好主意,它已被官方地列为了不引荐的做法,而且今后能够会移除这个接口。我们更引荐的是,运用多历程和自动重启。
我们一样不引荐运用domains
。它一般也并不能解决题目,而且已经是一个被标识为弃用的模块。
运用try-catch
Try-catch
是一个JavaScript
言语自带的捕捉同步代码的构造。运用try-catch
,你能够捕捉比方JSON剖析毛病如许的非常。
运用JSHint
或JSLint
如许的东西则能够让你阔别援用毛病或未定义变量这类隐式的非常。
一个运用try-catch
来防止历程退出的例子:
// Accepts a JSON in the query field named "params"
// for specifying the parameters
app.get('/search', function (req, res) {
// Simulating async operation
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');
}
})
});
然则,try-catch
只能捕捉同步代码的非常。然则Node.js
天下主如果异步的,所以,关于大多数的非常它都无计可施。
运用promise
Promise
能够经由过程then()
处置惩罚异步代码里的统统非常(显式和隐式)。记得在promise
链的末了加上.catch(next)
。例子:
app.get('/', function (req, res, next) {
// do some sync stuff
queryDb()
.then(function (data) {
// handle data
return makeCsv(data)
})
.then(function (csv) {
// handle csv
})
.catch(next)
})
app.use(function (err, req, res, next) {
// handle error
})
现在一切的同步代码和异步代码的非常都通报到了非常处置惩罚中间件中。
然则,仍有两点须要提示:
一切你的异步代码都必需返回一个promise
(除了emitter
)。假如你正在运用的库没有返回一个promise
,那末就运用一些东西要领(如Bluebird.promisifyAll()
)来转换它。Event emitter
(如stream
)仍会形成未处置惩罚的非常。所以你必需合理地监听它们的error
事宜。例子:
app.get('/', wrap(async (req, res, next) =>; {
let company = await getCompanyById(req.query.id)
let stream = getLogoStreamById(company.id)
stream.on('error', next).pipe(res)
}))
更多关于运用promise
处置惩罚非常的信息,请参阅这里。
须要对环境做的事
以下是一些你能够对你的体系环境做的事,用于提拔你运用的机能:
将
NODE_ENV
设置为“production”
保证你的运用在发作毛病后自动重启
运用集群形式运转你的运用
缓存要求效果
运用负载平衡
运用反向代办
将NODE_ENV
设置为“production”
NODE_ENV
环境变量指清楚明了运用当前的运转环境(开辟或临盆)。你能够做的为你的Express
提拔机能的最简朴的事变之一,就是将NODE_ENV
设置为“production”
。
将NODE_ENV
设置为“production”
将使Express
:
缓存视图模板
缓存CSS文件
天生更简约的毛病信息
假如你想写环境相干的代码,你能够经由过程process.env.NODE_ENV
来猎取运转时NODE_ENV
的值。不过须要注重的,搜检环境变量的值会形成少量的机能丧失,所以不要有太多这类操纵。
你能够已习惯了SHELL
中设置环境变量,比方运用export
或.bash_profile
文件。然则你不应当在你的临盆效劳器上这么做。你应当运用操纵体系的初始化体系(systemd
或systemd
)。下一个章节将会更仔细的报告初始化体系,然则因为设置NODE_ENV
是云云的主要以及简朴,所以我们在这里就列出它:
当运用Upstart
时,请在使命文件中运用env
关键字。例子:
# /etc/init/env.conf
env NODE_ENV=production
更多信息,请参阅这里。
当运用systemd
时,请在你的单位文件中运用Environment
指令。例子:
# /etc/systemd/system/myservice.service
Environment=NODE_ENV=production
更多信息,请参阅这里。
假如你正在运用StrongLoop Process Manager
,你也能够参阅这篇文章。
保证你的运用在发作毛病后自动重启
在临盆环境下,你肯定不愿望你的运用离线。所以你须要保证在你的运用发作毛病时或你的效劳器本身崩溃时,你的运用能够自动重启。虽然你能够不希冀它们的发作,然则我们须要更现实得防备它们,能够经由过程:
运用一个历程治理员(process manager)库来重启你的运用
当你的操纵体系崩溃时,运用它供应的初始化体系来重启你的历程治理员。
Node.js
运用在碰到未处置惩罚非常时就会退出。你的首要使命是保证你的代码的测试健全而且合理地处置惩罚了一切的非常。然则若有万一,请预备一个机制来确保它的自动重启。
运用历程治理员(process manager)
在开辟环境下,你能够简朴地运用node server.js
如许的敕令来启动你的运用。当时在临盆环境下这么做将是不被许可的。假如运用崩溃了,在你手动重启它之前,它都邑处于离线状况。为了保证你运用的自动重启,请运用一个历程治理员,它能够协助你治理正在运转的运用。
除了保证你的运用的自动重启,一个历程治理员还能够使你:
猎取当前运转环境的机能表现和资本斲丧状况。
自动地修正环境设置
治理集群(
StrongLoop PM
和pm2
)
Node.js
天下里比较盛行的历程治理员有:
StrongLoop Process Manager
PM2
Forever
更多的它们之间的比较,你能够参阅这里。关于它们三者的简介,你能够参阅这篇文章。
运用一个初始化体系
接下来要保证的就是,在你的效劳器重启时,你的运用也会响应的重启。只管我们以为我们的效劳器是非常稳固的,但它们仍有挂掉的能够。所以为了保证在你的效劳器时重启时你的运用也会重启,请运用你操纵体系内建的初始化体系。现在比较主流的是systemd
和Upstart
。
以下是经由过程你的Express
运用来运用初始化体系的两种要领:
将你的运用运转于一个历程治理员中,然后将历程治理员设置为体系的一个效劳。这个是比较引荐的做法。
直接经由过程初始化体系运转你的运用。这个要领越发简朴,但你却享用不到历程治理员带来的福利。
Systemd
Systems
是一个linux
体系的效劳治理员。大多数的linux
发行版都将它作为默许的初始化体系。
一个systems
效劳的设置文件也被称为一个单位文件,有一个.service
后缀。以下是一个直接治理Node.js
运用的例子:
[Unit]
Description=Awesome Express App
[Service]
Type=simple
ExecStart=<strong>/usr/local/bin/node /projects/myapp/index.js</strong>
WorkingDirectory=<strong>/projects/myapp</strong>
User=nobody
Group=nogroup
# Environment variables:
Environment=<strong>NODE_ENV=production</strong>
# Allow many incoming connections
LimitNOFILE=infinity
# Allow core dumps for debugging
LimitCORE=infinity
StandardInput=null
StandardOutput=syslog
StandardError=syslog
Restart=always
[Install]
WantedBy=multi-user.target
更多关于systemd
的信息,请参阅这里。
Upstart
Upstart
是一个大多数linux
发行版都可用的体系东西,用于在体系启动时启动使命和效劳,在体系封闭时住手它们,而且监控它们。你能够先将你的Express
运用或历程治理员设置为一个效劳,然后Upstart
会自动地在体系重启后重启它们。
一个Upstart
效劳被定义在一个使命设置文件中,有一个.conf
后缀。下面的例子展现了怎样建立一个名为“myapp”
的使命,且运用的进口是/projects/myapp/index.js
。
在/etc/init/
下建立一个名为myapp.conf
的文件:
# When to start the process
start on runlevel [2345]
# When to stop the process
stop on runlevel [016]
# Increase file descriptor limit to be able to handle more requests
limit nofile 50000 50000
# Use production mode
env <strong>NODE_ENV=production</strong>
# Run as www-data
setuid www-data
setgid www-data
# Run from inside the app dir
chdir <strong>/projects/myapp</strong>
# The process to start
exec <strong>/usr/local/bin/node /projects/myapp/index.js</strong>
# Restart the process if it is down
respawn
# Limit restart attempt to 10 times within 10 seconds
respawn limit 10 10
注重:这个剧本要求Upstart
1.4 或更新的版本,支撑于Ubuntu
12.04-14.10。
除了自动重启你的运用,Upstart
还为你供应了以下敕令:
start myapp – 手动启动运用
restart myapp – 手动重启运用
stop myapp – 手动退出运用
更多关于Upstart
的信息,请参阅这里。
运用集群形式运转你的运用
在多核的体系里,你能够经由过程启动一个历程集群来成倍了提拔你运用的机能。一个集群运转了你的运用的多个实例,抱负状况下,一个CPU核对应一个实例。如许,便能够在多个实例件举行负载平衡。
值得注重的是,因为运用实例跑在差别的历程里,所以它们并不分享统一块内存空间。因为,运用里的一切对象都是当地的,你不能够在运用代码里保护状况。不过,你能够运用如redis
如许的内存数据库来存储session
如许的数据和状况。
在集群中,一个事情历程的崩溃不会影响到其他的事情历程。所以除了机能要素以外,零丁事情历程崩溃的互相不影响也是另一个运用集群的优点。一个事情历程崩溃后,请确保纪录下日记,然后从新经由过程cluster.fork()
建立一个新的事情历程。
运用Node.js
的cluster
模块
Node.js
供应了cluster
模块来支撑集群。它使得一个主历程能够建立出多个事情历程。然则,比起直接运用这个模块,很多的库已为你封装了它,并供应了更多自动化的功用:如node-pm或cluser-service。
缓存要求效果
另一个提拔你运用机能的门路是缓存要求的效果,如许一来,关于统一个要求,你的运用就没必要做过剩的反复行动。
运用一个如Varnish
或Nginx
如许的缓存效劳器能够极大地提拔你运用的响应速度。
运用负载平衡
不管一个运用优化地何等好,一个零丁的实例老是有它的负载上限的。一个很好的解决办法就是将你的运用跑上多个实例,然后在它们之前加上一个负载平衡器。
一个负载平衡器一般是一个反向代办,它接收负载,并将其匀称得分配给各个实例或效劳器。你能够经由过程Nginx
或HAProxy
非常方便地架设一个负载平衡器。
运用了负载平衡后,你能够保证每一个要求都依据它的泉源被设置了奇特session id
。固然,你也能够运用如Redis
如许的内存数据库来存储session
。更多概况,能够参阅这里。
负载平衡是一个相称庞杂的话题,越发仔细的议论已超过了本文的领域。
运用反向代办
一个反向代办被设置与web运用之前,用于支撑各种关于要求的操纵,如将要求发送给运用,自动处置惩罚毛病页,紧缩,缓存,供应静态文件,负载平衡,等等。
在临盆环境中,这里引荐将Express
运用跑在Nginx
或HAProxy
以后。