【译】Node.js 最佳实践

header

[译]Node.js 最佳实践

本文翻译自Node.js Best Practices ,以下是译文,有不对的地方,欢迎留言指正。

Node.js最近几年已经成为最流行的平台之一。Node.js项目非常的容易上手,但是一旦你写完Hello World的app,知道如何最好地构建代码并且如何处理错误有时会成为一个噩梦(与大多数语言和框架一样)。
不幸的是,这个噩梦使得坚固的应用程序跟产生灾难有所区别。

虽说如此,但是让我们看一些针对Node.js最佳实践,能够帮助你跳出一些Node.js的坑。

1. 使用npm init初始化你的所有项目

很多人都知道NPM是一种安装依赖包的工具,但它远不止如此。我强烈建议大家用npm init创建一个项目,如下所示:

1
2
3
$ mkdir my-new-project
$ cd my-new-project
$ npm init

执行完后,会生成一个文件package.json,为你生成一些初始设置数据,帮助其他在项目上工作的人有与您相同的设置。

例如,我经常打开package.json并添加一个指定运行的Node.js的版本,代码如下:

1
2
3
"engines": {
"node": "6.2.0"
}

2. 创建.npmrc

如果你以前用过npm,就一定对--save这个标记不陌生,它代表安装第三方包并更新到package.json中。当其他开发人员克隆项目的时候,可以确保具有正确的依赖关系。但是,不是每次安装包的时候都能记得添加这个标记。

另外,NPM会在每个版本前面添加符号^,当有人运行npm install,和你相比,他们会得到包的不同版本。虽然更新包是一个很好的做法,但是有的开发人员在不同版本上运行,从而得到不同的结果或API的可用性。

因此,最好的方式是,让每个人都使用相同的版本。为了达到这目的,我们发现’.npmrc’文件有一些有用的属性,可以确保每次npm install都能更新package.json,并且强制安装依赖性的版本一致。

你只需要在你的终端运行:

1
2
$ npm config set save=true
$ npm config set save-exact=true

现在当前运行 npm install,你的依赖将会保存并且将锁定你安装的版本。

3. 在package.json中添加脚本

所有的程序都需要一个启动脚本。来让程序知道哪个文件先调用,哪个参数是在某些项目中特别重要。这些NPM都为你想到了。

简单的添加一个scripts在你的package.json中,并增加一个start的key。这个的意思是以一个命令启动你的程序。上代码:

1
2
3
"scripts": {
"start": "node myapp.js"
}

一旦有人运行npm start,NPM将会运行node myapp.js,并从node_modules/.bin找到所有的$PATH变量运行。这就意味着,你将不用把所有的包进行全局安装。

这里还有一些其他的例子值得参考:

1
2
3
4
5
"scripts": {
"postinstall": "bower install && grunt build",
"start": "node myapp.js",
"test": "node ./node_modules/jasmine/bin/jasmine.js"
}

运行npm install后就会运行postinstall。还有postinstall里可以填写任意,你想在NPM包安装完以后的操作。

当有人运行npm test将会触发test脚本,这是一个好的方法去进行测试,而不用关心你是用Jasmine,Mocha,Selenium,还是etc。

你也可以在这里自定义你自己的脚本。运行就用npm run scriptname

4. 使用环境变量

每个语言都有配置文件。不同环境使用不同的数据库,服务等。

Node.js推荐的方法是使用环境变量,在代码中用process.env来获取。例如,打印出你正在使用的环境变量,并查看NODE_ENV的值:

1
console.log("Running in :"  + process.env.NODE_ENV);

这是现在大多数云托管商在用的一个标准变量名称。

如果想查看更多的配置,请参考
https://github.com/indexzero/nconf.

另外一个挺受欢迎的加载环境变量的是
https://github.com/motdotla/dotenv (谢谢@szabi)

5. 使用编程风格规范

我们都经历过接受别人项目,查看别人的代码,然后花费一个小时格式化代码,替换tab符。 导致整个问题的一是,每个人都有自己开发风格,在加上组织或者公司没有一套标准的开发方式规范。

如果所有的代码都按照一种风格编写,那么将会更易理解,减少是使用tab还是空格的时间。如果编程风格是规定的(并且使用JSHint,ESlint或JSCS强制执行),这样的代码更易管理。

你不必自己去写规则,有的时候借鉴别人的规则能事半功倍。这里有一些好的例子:

选择一个,并用起来吧!

6. 拥抱 async

我相信大家都听过 promises的炒作了吧,甚至听到一些async/awaitgenerators在ES2016中。所有技术的背后,关键思想就是让你的代码能够异步执行。

JavaScript中有方法同步执行的问题,它将阻碍其他代码运行,直到它们完成。但是同步执行代码更易理解。另外,异步结构如promises其实会处理一些东西来让你的代码免受阻塞。

首先,我强烈建议你使用--trace-sync-io标记来运行你的程序(仅在开发环境中)。当程序使用同步API时,这将打印一个讲个跟堆栈跟踪。

有很多关于如何使用的promises, generatorsasync/await的文章,我不用复制粘贴,给你地址,自己看:

7. 处理 errors

生产环境中出现error,瞬间让你的app体验变差。好的异常处理对弈任何app都很重要,并且处理错误的最佳方法是使用上面的异步编程。例如,promises提供的.catch()方法,干净的处理捕捉到的所有错误。

加入你有一串promises,并且任何一个都可能突然挂掉,你可以用下面的方式非常简单的处理这些error:

1
2
3
4
5
6
doSomething()
.then(doNextStage)
.then(recordTheWorkSoFar)
.then(updateAnyInterestedParties)
.then(tidyUp)
.catch(errorHandler);

在上面的例子中,哪个函数失败都不重要,任何一个发生错误都将出现在errorHandler中。

8. 确保你的应用程序可以自动重启

ok,当你遵循上述的最佳实践来处理你的errors,但是,不幸的是仍然有一些错误出现,让你的程序挂掉😦。

如何让你的程序在发生错误的时候快速重启。如何让你的程序在运行的时候快速重启。如何让你的程序在机器重启的时候快速启动。

我建议用PM2http://pm2.keymetrics.io/ 来管理你的进程.其他的还有 Nodemon(thanks @szabi) 和 Forever

1

首先,全局安装这个模块:

1
$ npm install pm2 -g

然后启动你的进程:

1
$ pm2 start myApp.js

其他的例如服务器崩溃重新启动,你可以看PM2使用说明:

9. 使用集群,提高你程序的可靠性跟性能

默认情况下,Node.js在单进程中运行。实际上,你想运行多个进程来进行负载。这提高了web程序的处理HTTP请求和性能的可扩展性。除此之外,如果你的一个进程崩溃了,其他的仍然能处理请求。

使用PM2这样的进程管理的好处就是支持集群。

要想让你的程序启动多个实例,你只需运行:

1
$ pm2 start myApp.js -i max

需要注意的是,没个进程都是独立的,他们不共享内存或资源。例如,每个进程都将打开它自己与数据库的连接。Redis可以共享session状态。

10. 所有的导入依赖包写在文件最前面

我经常看到一些程序员这么这样的代码:

1
2
3
4
5
6
app.get("/my-service", function(request, response) {
var datastore = require("myDataStoreDep")(someConfig);

datastore.get(req.query.someKey)
// etc, ...
});

上面这段代码的问题是,当有人请求/my-service时, 代码将会从myDataStoreDep加载所有的文件,任何一个都有可能抛出异常。另外,当传递配置文件时,这个错误使这个进程崩溃。此外,我们不知道资源同步需要多长时间。在这段代码中,我们阻止了其他所有的请求被处理。

所以你应该总是预先加载所有的依赖项,然后配置它们。这样一来,您将从启动中知道是否存在问题,而不是在您的应用程序投入使用后三到四个小时!

11. 使用日志库来重现错误

console.log是非常好的记录手段,但是在生产环境被限制使用。在成千上万条日志中找出错误的原因…我相信你肯定遇到过,真尼玛痛苦。

一个成熟的日志库可以解救你。首先,它允许你设置日志的等级debug,info,warning,error,然后,它还允许你选择记录的方式,是记录文件还是数据库。

在我的程序中,我通常使用Loggly, Loggly能够让我使用图形化界面快速搜索。此外,它还允许我配置阈值开关,例如,如果我的程序开始返回500 SERVER ERROR消息给用户超过30秒,Loggly可以给我发送一条消息让我知道发生了什么。

3

12.在web应用中使用Helmet

如果你写web应用,你应该遵循很多常见的最佳实践来保护你的程序:

  • XSS 保护
  • 防止点击使用 X-Frame-Options
  • 所有连接使用 HTTPS
  • 在header中使用Context-Security-Policy
  • 禁用X-Powered-By头,使攻击者无法将其攻击缩小到特定的软件

Helmet不会记得配置所有这些headers,而是将它们全部设置为明确的默认值,并允许您调整所需的默认值。

Helmet

在Express.js应用程序上设置非常简单:

1
$ npm install helmet

然后在你的代码中配置:

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

13. 监控你的应用程序

在应用程序出现问题时收到通知,对于生产应用程序至关重要。你不想检查你的Twitter feed,看到成千上万的愤怒的用户告诉你你的服务器崩溃,或者你的应用程序坏了,而且已经过去几个小时了。因此,对关键问题或异常行为进行监控和提醒是非常重要的。

我们已经讨论了PM2进程管理。此外,它为开发人员开发了一个KeyMetrics.io的SaaS平台,用来监控程序,能够跟PM2配合使用。这是非常简单的,针对很多开发者他们有一个免费的计划。一旦你注册了KeyMetrics,你可以简单地运行:

1
$ pm2 interact [public_key] [private_key] [machine_name]

它会发送内存和CPU使用情况,以及关键指标服务器的异常报告,在它们的首页可以看到。你还可以看到http请求的延迟或在出现问题时设置事件(例如下载包超时)。

latency

另外,Loggly(我们之前提到的)也提供了基于监视的关闭日志。这两种工具的组合可以为您提供一种在出现故障之前快速对问题做出反应的方法。

14. 测试你的代码

Yeah, yeah, yeah - I know I should be testing. TDD and all that jazz!

测试会多次挽救你的程序,像培养一个习惯一样,开始并持续做是痛苦的。它影响你的开发进度。但是,凭借经验来说,一旦没有测试的代码产生问题,你希望能快速解决它。

无论您在一个项目中处于什么阶段,开始接入测试都不会太晚。我的建议开始小,开始简单。我也强烈建议为每个bug撰写测试。这样你知道:

  • 如何重现错误(确保您的测试首先失败!)
  • 修复错误(确保在修复问题后测试通行证)
  • 该错误将永远不会再发生(确保您在每个新的部署中运行测试)

有很多的测试库,我个人推荐Jasmine 因为我已经使用了很长时间,但是 Mocha, chai 或者其他的库也是很好的。如果你正在写web程序,我强烈建议你 使用Supertest黑盒子来测试你的程序。

总结

女士们,先生们,这就是我提名的 Node.js“TOP 14 最佳实践”。
如果您想提名一个额外的Node.js最佳实践,请留言。让我们在Node.js项目永存。

坚持原创技术分享,您的支持将鼓励我继续创作!