forked from goldbergyoni/nodebestpractices
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request goldbergyoni#16 from MattJin/chinese-translation
[Chinese translation]merge into master
- Loading branch information
Showing
37 changed files
with
2,099 additions
and
15 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# 使用 ESLint 和 Prettier | ||
|
||
|
||
### 比较 ESLint 和 Prettier | ||
|
||
如果你使用ESLint格式化代码,它只是给你一个警告,比如这一行太宽(取决于你的`最大长度`设置)。Prettier会自动为你格式化。 | ||
|
||
```javascript | ||
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne(), noWayYouGottaBeKiddingMe()); | ||
``` | ||
|
||
```javascript | ||
foo( | ||
reallyLongArg(), | ||
omgSoManyParameters(), | ||
IShouldRefactorThis(), | ||
isThereSeriouslyAnotherOne(), | ||
noWayYouGottaBeKiddingMe() | ||
); | ||
``` | ||
|
||
Source: [https://github.com/prettier/prettier-eslint/issues/101](https://github.com/prettier/prettier-eslint/issues/101) | ||
|
||
### Integrating ESLint and Prettier | ||
|
||
ESLint和Prettier在代码格式化功能上有重叠, 但它可以很容易通过其他的包来解决,比如 [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), and [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)。 有关他们的差异的更多信息,您可以查看链接 [here](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# 使用 APM 产品发现错误和宕机时间 | ||
|
||
|
||
### 一段解释 | ||
|
||
异常 != 错误。传统的错误处理假定存在异常,但应用程序错误可能以代码路径慢,API停机,缺少计算资源等形式出现。因为APM产品允许使用最小的设置来先前一步地检测各种各样 "深埋" 的问题,这是运用它们方便的地方。APM产品的常见功能包括: 当HTTP API返回错误时报警, 在API响应时间低于某个阈值时能被检测, 觉察到‘code smells’,监视服务器资源,包含IT度量的操作型智能仪表板以及其他许多有用的功能。大多数供应商提供免费方案。 | ||
|
||
### 关于 APM 的维基百科 | ||
|
||
在信息技术和系统管理领域, 应用程序性能管理(APM)是对软件应用程序的性能和可用性的监视和管理。APM努力检测和诊断复杂的应用程序性能问题, 以维护预期的服务级别。APM是"将IT度量标准转换为业务含义" | ||
|
||
### 了解 APM 市场 | ||
|
||
APM 产品由3个主要部分构成: | ||
|
||
1. 网站或API监控 – 通过HTTP请求不断监视正常运行时间和性能的外部服务。可以在几分钟内安装。以下是少数选定的竞争者: [Pingdom](https://www.pingdom.com/), [Uptime Robot](https://uptimerobot.com/), 和[New Relic](https://newrelic.com/application-monitoring); | ||
|
||
2. 代码检测 – 这类产品需要在应用程序中嵌入代理, 以实现如检测运行缓慢的代码、异常统计、性能监视等功能。以下是少数选定的竞争者: New Relic, App Dynamics; | ||
|
||
3. 操作型智能仪表板 – 这些产品系列侧重于为ops团队提供度量和管理内容, 帮助他们轻松地保持应用程序性能维持在最佳状态。这通常涉及聚合多个信息源 (应用程序日志、DB日志、服务器日志等) 和前期仪表板设计工作。以下是少数选定的竞争者: [Datadog](https://www.datadoghq.com/), [Splunk](https://www.splunk.com/), [Zabbix](https://www.zabbix.com/); | ||
|
||
|
||
### 示例: UpTimeRobot.Com – 网站监控仪表板 | ||
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") | ||
|
||
### 示例: AppDynamics.Com – 与代码检测结合的端到端监视 | ||
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# 对于异步的错误处理,请使用Async-Await或者promise | ||
|
||
|
||
### 一段解释 | ||
|
||
由于回调对于大多数的程序员来说不熟悉,被迫随处检测错误,让人不快的代码内嵌和难以理解的代码流程,它没有形成一定规模。promise的库,比如BlueBird,async,和Q封装了一种标准的代码风格, 它通过使用return和throw来控制程序流程。具体来说,它们支持最受欢迎的try-catch错误处理风格,这使得主流程代码从在每一个方法中处理错误的方式中解放出来。 | ||
|
||
|
||
### 代码示例 – 使用promise捕获错误 | ||
|
||
|
||
```javascript | ||
doWork() | ||
.then(doWork) | ||
.then(doOtherWork) | ||
.then((result) => doWork) | ||
.catch((error) => {throw error;}) | ||
.then(verify); | ||
``` | ||
|
||
### 代码示例 反模式 – 回调方式的错误处理 | ||
|
||
```javascript | ||
getData(someParameter, function(err, result){ | ||
if(err != null) | ||
//做一些事情类似于调用给定的回调函数并传递错误 | ||
getMoreData(a, function(err, result){ | ||
if(err != null) | ||
//做一些事情类似于调用给定的回调函数并传递错误 | ||
getMoreData(b, function(c){ | ||
getMoreData(d, function(e){ | ||
if(err != null) | ||
//你有什么想法? | ||
}); | ||
}); | ||
``` | ||
### 博客引用: "我们使用promise有一个问题" | ||
摘自博客pouchdb.com | ||
> ……实际上, 回调会做一些更险恶的事情: 他们剥夺了我们的stack, 这是我们通常在编程语言中想当然的事情。编写没有堆栈的代码很像驾驶一辆没有刹车踏板的汽车: 你没有意识到你有多么需要它, 直到你伸手去找它, 而它不在那里。promise的全部目的是让我们回到我们在异步时丢失的语言基础: return,throw和stack。但你必须知道如何正确使用promise, 以便利用他们。 | ||
### 博客引用: "promise方法更加紧凑" | ||
摘自博客gosquared.com | ||
> ………promise的方法更紧凑, 更清晰, 写起来更快速。如果在任何ops中发生错误或异常,则由单个.catch()处理程序处理。有这个单一的地方来处理所有的错误意味着你不需要为每个阶段的工作写错误检查。 | ||
### 博客引用: "原生ES6支持promise,可以和generator一起使用" | ||
摘自博客StrongLoop | ||
> ….回调有一个糟糕的错误处理的报道。promise更好。将express内置的错误处理与promise结合起来, 大大降低了uncaught exception的几率。原生ES6支持promise, 通过编译器babel,它可以与generator,ES7提议的技术(比如async/await)一起使用。 | ||
### 博客引用: "所有那些您所习惯的常规的流量控制结构, 完全被打破" | ||
摘自博客Benno’s | ||
> ……关于基于异步、回调编程的最好的事情之一是, 基本上所有那些您习惯的常规流量控制结构, 完全被打破。然而, 我发现最易打破的是处理异常。Javascript提供了一个相当熟悉的try...catch结构来处理异常。异常的问题是, 它们提供了在一个调用堆栈上 short-cutting错误的很好的方法, 但最终由于不同堆栈上发生的错误导致完全无用… |
58 changes: 58 additions & 0 deletions
58
sections/errorhandling/catchunhandledpromiserejection.chinese.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# 捕获未处理的promise rejections | ||
<br/><br/> | ||
|
||
|
||
### 一段解释 | ||
|
||
通常,大部分的现代node.js/express应用代码运行在promise里 – 或者是在.then里处理,一个回调函数中,或者在一个catch块中。令人惊讶的是,除非开发者记得添加.catch语句,在这些地方抛出的错误都不会被uncaughtException事件处理程序来处理,然后消失掉。当未处理的rejection出现,node最近的版本增加了一个警告消息,尽管当事情出错的时候这可能有助于发现问题,但这显然不是一个适当的错误处理方法。简单明了的解决方案是永远不要忘记在每个promise链式调用中添加.catch语句,并重定向到一个集中的错误处理程序。然而,只在开发人员的规程上构建错误处理策略是有些脆弱的。因此,使用一个优雅的回调并订阅到process.on('unhandledrejection',callback)是高度推荐的 – 这将确保任何promise错误,如果不是本地处理,将在这处理。 | ||
|
||
<br/><br/> | ||
|
||
### 代码示例: 这些错误将不会得到任何错误处理程序捕获(除unhandledrejection) | ||
|
||
```javascript | ||
DAL.getUserById(1).then((johnSnow) => | ||
{ | ||
//this error will just vanish | ||
if(johnSnow.isAlive == false) | ||
throw new Error('ahhhh'); | ||
}); | ||
|
||
``` | ||
<br/><br/> | ||
### 代码示例: 捕获 unresolved 和 rejected 的 promise | ||
|
||
```javascript | ||
process.on('unhandledRejection', (reason, p) => { | ||
//我刚刚捕获了一个未处理的promise rejection, 因为我们已经有了对于未处理错误的后备的处理机制(见下面), 直接抛出,让它来处理 | ||
throw reason; | ||
}); | ||
process.on('uncaughtException', (error) => { | ||
//我刚收到一个从未被处理的错误,现在处理它,并决定是否需要重启应用 | ||
errorManagement.handler.handleError(error); | ||
if (!errorManagement.handler.isTrustedError(error)) | ||
process.exit(1); | ||
}); | ||
|
||
``` | ||
<br/><br/> | ||
### 博客引用: "如果你犯了错误,在某个时候你就会犯错误。" | ||
摘自 James Nelson 的博客 | ||
|
||
> 让我们测试一下您的理解。下列哪一项是您期望错误将会打印到控制台的? | ||
```javascript | ||
Promise.resolve(‘promised value’).then(() => { | ||
throw new Error(‘error’); | ||
}); | ||
|
||
Promise.reject(‘error value’).catch(() => { | ||
throw new Error(‘error’); | ||
}); | ||
|
||
new Promise((resolve, reject) => { | ||
throw new Error(‘error’); | ||
}); | ||
``` | ||
|
||
> 我不知道您的情况,但我的回答是我希望它们所有都能打印出一个错误。然而,现实是许多现代JavaScript环境不会为其中任何一个打印错误。做为人的问题是,如果你犯了错误,在某个时候你就会犯错误。记住这一点,很显然,我们应该设计这样一种方式,使错误尽可能少创造伤害,这意味着默认地处理错误,而不是丢弃错误。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# 集中处理错误,通过但不是在中间件里处理错误 | ||
|
||
|
||
### 一段解释 | ||
|
||
如果没有一个专用的错误处理对象,那么由于操作不当,在雷达下重要错误被隐藏的可能性就会更大。错误处理对象负责使错误可见,例如通过写入一个格式化良好的logger,通过电子邮件将事件发送到某个监控产品或管理员。一个典型的错误处理流程可能是:一些模块抛出一个错误 -> API路由器捕获错误 -> 它传播错误给负责捕获错误的中间件(如Express,KOA)-> 集中式错误处理程序被调用 -> 中间件正在被告之这个错误是否是一个不可信的错误(不是操作型错误),这样可以优雅的重新启动应用程序。注意,在Express中间件中处理错误是一种常见但又错误的做法,这样做不会覆盖在非Web接口中抛出的错误。 | ||
|
||
|
||
|
||
### 代码示例 – 一个典型错误流 | ||
|
||
```javascript | ||
//DAL层, 在这里我们不处理错误 | ||
DB.addDocument(newCustomer, (error, result) => { | ||
if (error) | ||
throw new Error("Great error explanation comes here", other useful parameters) | ||
}); | ||
|
||
//API路由代码, 我们同时捕获异步和同步错误,并转到中间件 | ||
try { | ||
customerService.addNew(req.body).then(function (result) { | ||
res.status(200).json(result); | ||
}).catch((error) => { | ||
next(error) | ||
}); | ||
} | ||
catch (error) { | ||
next(error); | ||
} | ||
|
||
//错误处理中间件,我们委托集中式错误处理程序处理错误 | ||
app.use(function (err, req, res, next) { | ||
errorHandler.handleError(err).then((isOperationalError) => { | ||
if (!isOperationalError) | ||
next(err); | ||
}); | ||
}); | ||
|
||
``` | ||
|
||
### 代码示例 – 在一个专门的对象里面处理错误 | ||
|
||
```javascript | ||
module.exports.handler = new errorHandler(); | ||
|
||
function errorHandler(){ | ||
this.handleError = function (error) { | ||
return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError); | ||
} | ||
|
||
``` | ||
### 代码示例 – 反模式:在中间件内处理错误 | ||
```javascript | ||
//中间件直接处理错误,那谁将处理Cron任务和测试错误呢? | ||
app.use(function (err, req, res, next) { | ||
logger.logError(err); | ||
if(err.severity == errors.high) | ||
mailer.sendMail(configuration.adminMail, "Critical error occured", err); | ||
if(!err.isOperational) | ||
next(err); | ||
}); | ||
|
||
``` | ||
### 博客引用: "有时较低的级别不能做任何有用的事情, 除非将错误传播给他们的调用者" | ||
摘自博客 Joyent, 对应关键字 “Node.JS error handling” 排名第一 | ||
> …您可能会在stack的多个级别上处理相同的错误。这发生在当较低级别不能执行任何有用的操作,除了将错误传播给它们的调用方, 从而将错误传播到其调用方, 等等。通常, 只有top-level调用方知道适当的响应是什么, 无论是重试操作、向用户报告错误还是其他事情。但这并不意味着您应该尝试将所有错误报告给单个top-level回调, 因为该回调本身无法知道错误发生在什么上下文中… | ||
### 博客引用: "单独处理每个错误将导致大量的重复" | ||
摘自博客 JS Recipes, 对应关键字 “Node.JS error handling” 排名17 | ||
> ……仅仅在Hackathon启动api.js控制器中, 有超过79处重复的错误对象。单独处理每个错误将导致大量的代码重复。您可以做的下一个最好的事情是将所有错误处理逻辑委派给一个express中间件… | ||
### 博客引用: "HTTP错误不会在数据库代码中出现" | ||
摘自博客 Daily JS, 对应关键字 “Node.JS error handling” 排名14 | ||
> ……您应该在error对象中设置有用的属性, 但使用此类属性时应保持一致。而且, 不要越过流: HTTP错误不会在数据库代码中出现。或者对于浏览器开发人员来说, Ajax 错误在与服务器交互的代码中有一席之地, 而不是处理Mustache模板的代码… | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# 使用Swagger对API错误文档化 | ||
|
||
|
||
### 一段解释 | ||
|
||
REST API使用HTTP代码返回结果, API用户不仅绝对需要了解API schema, 而且还要注意潜在错误 – 调用方可能会捕获错误并巧妙地处理它。例如, 您的api文档可能提前指出, 当客户名称已经存在时, HTTP状态409将返回 (假设api注册新用户), 因此调用方可以相应地呈现给定情况下的最佳UX。Swagger是一个标准, 它定义了 API 文档的schema, 提供了一个生态系统的工具, 允许在线轻松创建文档, 请参阅下面的打印屏幕。 | ||
|
||
### 博客引用: "您必须告诉您的调用者什么错误可能发生" | ||
摘自博客 Joyent, 对于关键字 “Node.JS logging” , 排名第一 | ||
|
||
> 我们已经讨论了如何处理错误, 但是在编写新函数时, 如何将错误传递给调用您的函数的代码? | ||
...如果你不知道会发生什么错误或者不知道他们的意思, 那么你的程序就不可能是正确的, 除非是偶然的。所以, 如果你正在写一个新的函数, 你必须告诉你的调用者什么错误可以发生, 它们的意思是什么… | ||
|
||
|
||
### 有用的工具: Swagger 在线文档创建工具 | ||
![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# 快速报错,使用专用库验证参数 | ||
|
||
|
||
### 一段解释 | ||
|
||
我们都知道如何检查参数和快速报错对于避免隐藏的错误很重要(见下面的反模式代码示例)。如果没有,请阅读显式编程和防御性编程。在现实中,由于对其编码是件恼人的事情(比如考虑验证分层的JSON对象,它包含像email和日期这样的字段),我们倾向于避免做这样的事情 – 像Joi这样的库和验证器轻而易举的处理这个乏味的任务。 | ||
|
||
### 维基百科: 防御性编程 | ||
|
||
防御性编程是一种改进软件和源代码的方法, 在以下方面: 一般质量 – 减少软件 bug 和问题的数量。使源代码可理解 – 源代码应该是可读的和可理解的, 以便在代码审核中得到批准。尽管会有意外输入或用户操作, 但使软件的行为具有可预知的方式。 | ||
|
||
|
||
|
||
### 代码示例: 使用‘Joi’验证复杂的JSON输入 | ||
|
||
```javascript | ||
var memberSchema = Joi.object().keys({ | ||
password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/), | ||
birthyear: Joi.number().integer().min(1900).max(2013), | ||
email: Joi.string().email() | ||
}); | ||
|
||
function addNewMember(newMember) | ||
{ | ||
//assertions come first | ||
Joi.assert(newMember, memberSchema); //throws if validation fails | ||
//other logic here | ||
} | ||
|
||
``` | ||
|
||
### 反模式: 没有验证会产生令人讨厌的错误 | ||
|
||
```javascript | ||
//假如折扣为正,重定向用户去打印他的折扣优惠劵 | ||
function redirectToPrintDiscount(httpResponse, member, discount) | ||
{ | ||
if(discount != 0) | ||
httpResponse.redirect(`/discountPrintView/${member.id}`); | ||
} | ||
|
||
redirectToPrintDiscount(httpResponse, someMember); | ||
//忘记传递参数discount, 为什么用户被重定向到折扣页面? | ||
|
||
``` | ||
|
||
### 博客引用: "您应该立即抛出这些错误" | ||
摘自博客: Joyent | ||
|
||
> 一个退化情况是有人调用一个异步函数但没有传递一个回调方法。你应该立即抛出这些错误, 因为程序有了错误, 最好的调试它的时机包括,获得至少一个stack trace, 和理想情况下,核心文件里错误的点。为此, 我们建议在函数开始时验证所有参数的类型。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# 监控 | ||
|
||
|
||
### 一段解释 | ||
|
||
> 在最基本的层面上,监控意味着您可以很*容易地识别出在生产环境中发生了什么不好的事情,例如,通过电子邮件或Slack通知。挑战在于选择合适的工具集来满足您的需求而不会破坏您的防护。我建议,从确定核心的指标集开始,这些指标必须被监控以确保一个健康的状态 – CPU,服务器的RAM,node进程RAM(小于1.4GB),在最后一分钟的错误量,进程重新启动的数量,平均响应时间。然后浏览一些你可能喜欢的高级功能并添加到你的愿望清单中。一些豪华的监控功能的例子:数据库分析,跨服务的测量(即测量业务交易),前端整合,暴露原始数据给自定义的BI clients,Slack通知及其他。 | ||
实现高级功能需要冗长的设置或购买商业产品如datadog,NewRelic和相似产品。不幸的是,即使是基本的实现也不像在公园散步那么简单,因为一些度量指标是硬件相关的(CPU),而其他的则在node进程内(内部错误),因此所有直接了当的工具都需要一些额外的设置。例如,云供应商监控解决方案(如AWS CloudWatch,谷歌Stackdriver)将立即告诉你关于硬件度量,但对内部应用程序的行为却无可奉告。在另一端,基于日志的解决方案如Elasticsearch默认情况下缺少hardware view。解决方案是用缺少的指标来增加您的选择,例如,一个流行的选择是将应用程序日志发送到Elastic stack,并配置一些额外的代理(如Beat)来共享与硬件相关的信息以获得完整的画面。 | ||
|
||
### 博客引用: "我们对于promise有一个问题" | ||
摘自博客 pouchdb.com, 对于关键字“node promises”,排名11 | ||
|
||
> … 我们建议您为所有服务监视这些信号: | ||
错误率:因为错误是面向用户的,并且会立即影响您的客户。 | ||
响应时间:因为延迟直接影响您的客户和业务。 | ||
吞吐量:流量有助于您了解错误率增加和延迟的上下文。 | ||
饱和度:它告诉您的服务负载多少。如果CPU使用率是90%,你的系统能处理更多的流量吗? | ||
… |
Oops, something went wrong.