mirror of
https://github.com/goldbergyoni/nodebestpractices.git
synced 2025-10-29 00:19:14 +08:00
Merge pull request #441 from oshliaer/russian-translation-02step
Russian translation
This commit is contained in:
@ -118,7 +118,7 @@
|
||||
|
||||
**Иначе:** Стиль обратного вызова Node.js, функция (err, response), является многообещающим способом создания непригодного для использования кода из-за сочетания обработки ошибок со случайным кодом, чрезмерных вложений и слабых шаблонов кодирования.
|
||||
|
||||
🔗 [**Подробнее: уход от обратных вызовов**](/section/errorhandling/asyncerrorhandling.md)
|
||||
🔗 [**Подробнее: Используйте Async-Await или обещания для асинхронной обработки ошибок**](/section/errorhandling/asyncerrorhandling.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -128,7 +128,7 @@
|
||||
|
||||
**Иначе:** При вызове какого-либо компонента нельзя быть уверенным, какой тип ошибок приходит в ответ -- это значительно затрудняет правильную обработку ошибок. Хуже того, использование пользовательских типов для описания ошибок может привести к потере информации о критических ошибках, таких как трассировка стека!
|
||||
|
||||
🔗 [**Подробнее: использование встроенного объекта ошибки**](/section/errorhandling/useonlythebuiltinerror.md)
|
||||
🔗 [**Подробнее: Используйте только встроенный объект Error**](/section/errorhandling/useonlythebuiltinerror.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -138,7 +138,7 @@
|
||||
|
||||
**Иначе:** Вы всегда можете перезапустить приложение, когда появляется ошибка, но зачем подводить ~5000 онлайн-пользователей из-за незначительной, прогнозируемой, операционной ошибки? Обратное также не идеально -- поддержание приложения в том случае, если возникла неизвестная проблема (ошибка программиста), может привести к непредсказуемому поведению. Разграничение между ними позволяет действовать тактично и применять сбалансированный подход, основанный на данном контексте.
|
||||
|
||||
🔗 [**Подробнее: операционная или программистская ошибка**](/sections/errorhandling/operationalvsprogrammererror.md)
|
||||
🔗 [**Подробнее: Различайте операционные и программистские ошибки**](/sections/errorhandling/operationalvsprogrammererror.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -148,7 +148,7 @@
|
||||
|
||||
**Иначе:** Необработка ошибок в одном месте приведет к дублированию кода и, возможно, к ошибкам, обработанным неправильно.
|
||||
|
||||
🔗 [**Подробнее: обработка ошибок в централизованном месте**](/sections/errorhandling/centralizedhandling.md)
|
||||
🔗 [**Подробнее: Обрабатывайте ошибки централизованно. Не в промежуточных слоях**](/sections/errorhandling/centralizedhandling.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -158,7 +158,7 @@
|
||||
|
||||
**Иначе:** Клиент API может принять решение о сбое и перезапуске только потому, что он получил ошибку, которую он не может понять. Примечание: вызывающим абонентом вашего API можете быть и вы сами (очень типично для микросервисной среды)
|
||||
|
||||
🔗 [**Подробнее: документирование ошибок API в Swagger или GraphQL**](/sections/errorhandling/documentingusingswagger.md)
|
||||
🔗 [**Подробнее: Документироваие ошибок API при использовании Swagger или GraphQL**](/sections/errorhandling/documentingusingswagger.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -168,7 +168,7 @@
|
||||
|
||||
**Иначе:** Когда происходит незнакомое исключение, некоторый объект может быть в неисправном состоянии (например, источник событий, который используется глобально и больше не генерирует события из-за некоторого внутреннего сбоя), и все будущие запросы могут давать сбой или вести себя безумно.
|
||||
|
||||
🔗 [**Подробнее: закрытие процесса**](/sections/errorhandling/shuttingtheprocess.md)
|
||||
🔗 [**Подробнее: Изящно выходите из процесса, когда неизвестное случается**](/sections/errorhandling/shuttingtheprocess.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -178,7 +178,7 @@
|
||||
|
||||
**Иначе:** Сканирование через console.logs или вручную через грязный текстовый файл без запросов инструментов или приличного просмотра журнала может занять вас на работе до поздна.
|
||||
|
||||
🔗 [**Подробнее: использование надежного регистратора**](/sections/errorhandling/usematurelogger.md)
|
||||
🔗 [**Подробнее: Используйте проверенный логгер, чтобы увеличить видимость ошибок**](/sections/errorhandling/usematurelogger.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -188,7 +188,7 @@
|
||||
|
||||
**Иначе:** Без тестирования, будь то автоматически или вручную, вы не сможете полагаться на свой код для возврата правильных ошибок. Без значения ошибок -- нет обработки ошибок.
|
||||
|
||||
🔗 [**Подробнее: тестирование потоков ошибок**](/section/errorhandling/testingerrorflows.md)
|
||||
🔗 [**Подробнее: Тестируйте потоки ошибок с использованием вашей любимой тестовой среды**](/section/errorhandling/testingerrorflows.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -198,7 +198,7 @@
|
||||
|
||||
**Иначе:** Вы можете потратить огромные усилия на измерение производительности и времени простоя API, возможно, вы никогда самостоятельно не узнаете, какие части кода в реально сценарии самые медленные, и как они влияют на UX.
|
||||
|
||||
🔗 [**Подробнее: использование продуктов APM**](/section/errorhandling/apmproducts.md)
|
||||
🔗 [**Подробнее: Обнаружение ошибок и простоев с использованием продуктов APM**](/section/errorhandling/apmproducts.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -208,7 +208,7 @@
|
||||
|
||||
**Иначе:** Ваши ошибки будут проглочены и не оставят следов. Не о чем беспокоиться!
|
||||
|
||||
🔗 [**Подробнее: перехват необработанного отказа от обещания**](/sections/errorhandling/catchunhandledpromiserejection.md)
|
||||
🔗 [**Подробнее: Перехватывайте необработанные отказы от обещаний**](/sections/errorhandling/catchunhandledpromiserejection.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -218,7 +218,7 @@
|
||||
|
||||
**Иначе:** Учтите это -- ваша функция ожидает числовой аргумент "Скидка", который вызывающая сторона забывает передать, позже ваш код проверяет, если Скидка !=0 (сумма разрешенной скидки больше нуля), тогда она позволит пользователю пользоваться скидкой. О, Боже, какая неприятная ошибка! Видишь?
|
||||
|
||||
🔗 [**Подробнее: быстрый провал**](/sections/errorhandling/failfast.md)
|
||||
🔗 [**Подробнее: Быстро проваливайтесь, проверяя аргументы, используя выделенную библиотеку**](/sections/errorhandling/failfast.russian.md)
|
||||
|
||||
<br/><br/><br/>
|
||||
|
||||
@ -232,7 +232,7 @@
|
||||
|
||||
**Иначе:** Разработчики сосредоточатся на утомительных проблемах с интервалами и шириной линии, и время может быть потрачено впустую на продумывание стиля кода проекта.
|
||||
|
||||
🔗 [**Подробнее: использование ESLint и Prettier**](/section/codestylepractices/eslint_prettier.md)
|
||||
🔗 [**Подробнее: Использование ESLint и Prettier**](/section/codestylepractices/eslint_prettier.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -554,7 +554,7 @@ null == undefined // true
|
||||
|
||||
**Иначе:** Отказ === разочарованные клиенты. Просто.
|
||||
|
||||
🔗 [**Подробнее: Monitoring!**](/sections/production/monitoring.md)
|
||||
🔗 [**Подробнее: Мониторинг**](/sections/production/monitoring.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -574,7 +574,7 @@ null == undefined // true
|
||||
|
||||
**Иначе:** Ваш бедный одиночный поток будет занят выполнением инфраструктурных задач вместо того, чтобы работать с ядром приложения, и производительность будет соответственно снижаться.
|
||||
|
||||
🔗 [**Подробнее: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md)
|
||||
🔗 [**Подробнее: Делегируйте все возможное (например, gzip, SSL) обратному прокси**](/sections/production/delegatetoproxy.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -608,13 +608,13 @@ null == undefined // true
|
||||
|
||||
<br/><br/>
|
||||
|
||||
## ![✔] 5.7. Создать "конечную точку обслуживания"
|
||||
## ![✔] 5.7. Создавайте "конечную точку обслуживания"
|
||||
|
||||
**TL;DR:** Предоставьте набор информации, связанный с системой, например, использование памяти и REPL, и т.д. в защищенном API. Хотя настоятельно рекомендуется полагаться на стандартные инструменты и инструменты для боевых испытаний, некоторые ценные сведения и операции легче выполнять с помощью кода.
|
||||
|
||||
**Иначе:** Вы обнаружите, что выполняете много "диагностических развертываний" -- отправка кода в производство только для извлечения некоторой информации в диагностических целях.
|
||||
|
||||
🔗 [**Подробнее: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md)
|
||||
🔗 [**Подробнее: Создавайте конечную точку обслуживания**](/sections/production/createmaintenanceendpoint.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -624,7 +624,7 @@ null == undefined // true
|
||||
|
||||
**Иначе:** Вы можете потратить огромные усилия на измерение производительности и времени простоя API, возможно, вы никогда не узнаете, какие ваши самые медленные части кода в реальном сценарии и как они влияют на UX.
|
||||
|
||||
🔗 [**Подробнее: Discover errors and downtime using APM products**](/sections/production/apmproducts.md)
|
||||
🔗 [**Подробнее: Уверенный пользовательский опыт с продуктами APM**](/sections/production/apmproducts.russian.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -664,7 +664,7 @@ null == undefined // true
|
||||
|
||||
**Иначе:** Сбой на данном сервере приведет к простою приложения, а не просто к гибели неисправного компьютера. Более того, гибкость масштабирования станет более сложной из-за зависимости от конкретного сервера.
|
||||
|
||||
🔗 [**Подробнее: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md)
|
||||
🔗 [**Подробнее: Не прописывайтесь на постоянку, убивайте свои серверы почти каждый день**](/sections/production/bestateless.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -684,7 +684,6 @@ null == undefined // true
|
||||
|
||||
**Иначе:** Глядя на журнал ошибок производства без контекста -- что произошло раньше -- становится намного сложнее и медленнее рассуждать о проблеме.
|
||||
|
||||
🔗 [**Подробнее: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md)
|
||||
|
||||
<br/><br/>
|
||||
|
||||
@ -718,7 +717,7 @@ null == undefined // true
|
||||
|
||||
## ![✔] 5.18. Не маршрутизируйте журналы в приложении
|
||||
|
||||
**TL;DR:** Места назначения журналов не должны жестко кодироваться разработчиками в коде приложения, но вместо этого должны определяться средой исполнения, в которой выполняется приложение. Разработчики должны записывать журналы в `stdout` с помощью утилиты logger и затем позвольте среде выполнения (контейнер, сервер и т. д.) направить поток `stdout` в соответствующее место назначения (т.е. Splunk, Graylog, ElasticSearch и т.д.).
|
||||
**TL;DR:** Места назначения журналов не должны жестко кодироваться разработчиками в коде приложения, но вместо этого должны определяться средой исполнения, в которой выполняется приложение. Разработчики должны записывать журналы в `stdout` с помощью утилиты logger и затем позвольте среде выполнения (контейнер, сервер и т.д.) направить поток `stdout` в соответствующее место назначения (т.е. Splunk, Graylog, ElasticSearch и т.д.).
|
||||
|
||||
**Иначе:** Маршрутизация журналов обработки приложения === трудности масштабирования, потеря журналов, плохое разделение задач
|
||||
|
||||
@ -1041,7 +1040,7 @@ null == undefined // true
|
||||
|
||||
**Иначе:** Вам придется поддерживать менее эффективные проекты, где вы могли бы просто использовать то, что **уже** доступно или иметь дело с еще несколькими строками в обмен на еще несколько файлов.
|
||||
|
||||
🔗 [**Подробнее: Нативные методы вместо утилит**](/sections/performance/nativeoverutil.md)
|
||||
🔗 [**Подробнее: Предпочитайте нативные методы JS над пользовательскими утилитами, такими как Lodash**](/sections/performance/nativeoverutil.russian.md)
|
||||
|
||||
<br/><br/><br/>
|
||||
|
||||
|
||||
26
sections/codestylepractices/eslint_prettier.russian.md
Normal file
26
sections/codestylepractices/eslint_prettier.russian.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Использование ESLint и Prettier
|
||||
|
||||
|
||||
### Сравнение ESLint и Prettier
|
||||
|
||||
Если вы отформатируете код ниже с помощью ESLint, он просто выдаст вам предупреждение, что он слишком широкий (зависит от настроек `max-len`). 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)
|
||||
|
||||
### Интеграция ESLint и Prettier
|
||||
|
||||
ESLint и Prettier перекрываются в функции форматирования кода, но могут быть легко объединены с помощью других пакетов, таких как [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), и [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier). Для получения дополнительной информации об их различиях вы можете просмотреть ссылку [здесь](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint).
|
||||
28
sections/errorhandling/apmproducts.russian.md
Normal file
28
sections/errorhandling/apmproducts.russian.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Обнаружение ошибок и простоев с использованием продуктов APM
|
||||
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Исключение != Ошибка. Традиционная обработка ошибок предполагает наличие исключения, но ошибки приложения могут проявляться в виде медленных путей кода, времени простоя API, нехватки вычислительных ресурсов и многого другого. Именно здесь продукты APM пригодятся, поскольку они позволяют обнаруживать широкий спектр "скрытых" проблем с минимальной настройкой. Среди общих функций продуктов APM, например, оповещение, когда HTTP API возвращает ошибки, обнаружение, когда время отклика API падает ниже некоторого порога, обнаружение "запахов кода", функции для мониторинга ресурсов сервера, панель оперативной аналитики с IT-метриками и многие другие полезные функции. Большинство поставщиков предлагают бесплатные планы использования.
|
||||
|
||||
### Википедия о APM
|
||||
|
||||
В области управления информационными технологиями и системами Application Performance Management (APM) - это мониторинг и управление производительностью и доступностью программных приложений. APM стремится обнаруживать и диагностировать сложные проблемы производительности приложений, чтобы поддерживать ожидаемый уровень обслуживания. APM - это "перевод метрик ИТ в бизнес-значение ([то есть] ценность)". Основные продукты и сегменты.
|
||||
|
||||
### Понимание рынка APM
|
||||
|
||||
Продукты APM составляют 3 основных сегмента:
|
||||
|
||||
1. Мониторинг веб-сайтов или API - внешние сервисы, которые постоянно отслеживают время безотказной работы и производительность посредством HTTP-запросов. Можно настроить за несколько минут. Ниже приведены несколько избранных претендентов: [Pingdom](https://www.pingdom.com/), [Uptime Robot](https://uptimerobot.com/), and [New Relic](https://newrelic.com/application-monitoring).
|
||||
|
||||
2. Инструментарий кода - семейство продуктов, которое требует встраивания агента в приложение для использования таких функций, как медленное обнаружение кода, статистика исключений, мониторинг производительности и многое другое. Ниже приведены несколько выбранных претендентов: New Relic, App Dynamics.
|
||||
|
||||
3. Панель оперативных сведений - эта линейка продуктов направлена на то, чтобы упростить работу оперативной команды с помощью метрик и кураторского контента, которые помогают легко оставаться на вершине производительности приложений. Обычно это включает в себя объединение нескольких источников информации (журналы приложений, журналы БД, журналы серверов и т.д.) и предварительную работу по разработке панели мониторинга. Ниже приведены несколько избранных претендентов: [Datadog](https://www.datadoghq.com/), [Splunk](https://www.splunk.com/), [Zabbix](https://www.zabbix.com/).
|
||||
|
||||
|
||||
|
||||
### Пример: UpTimeRobot.Com - панель мониторинга сайта
|
||||

|
||||
|
||||
### Пример: AppDynamics.Com – сквозной мониторинг в сочетании с инструментарием кода
|
||||

|
||||
78
sections/errorhandling/asyncerrorhandling.russian.md
Normal file
78
sections/errorhandling/asyncerrorhandling.russian.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Используйте Async-Await или обещания для асинхронной обработки ошибок
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Обратные вызовы плохо масштабируются, так как большинство программистов не знакомы с ними. Они заставляют проверять ошибки повсюду, имеют дело с неприятным вложением кода и затрудняют анализ потока кода. Библиотеки Promise, такие как BlueBird, async и Q, упаковывают стандартный стиль кода, используя RETURN и THROW для управления потоком программы. В частности, они поддерживают любимый стиль обработки ошибок try-catch, который позволяет освободить путь к основному коду от ошибок в каждой функции
|
||||
|
||||
### Пример кода – использование обещаний для отлова ошибок
|
||||
|
||||
```javascript
|
||||
return functionA()
|
||||
.then((valueA) => functionB(valueA))
|
||||
.then((valueB) => functionC(valueB))
|
||||
.then((valueC) => functionD(valueC))
|
||||
.catch((err) => logger.error(err))
|
||||
.then(alwaysExecuteThisFunction())
|
||||
```
|
||||
|
||||
### Пример кода - использование async/await для отлова ошибок
|
||||
|
||||
```javascript
|
||||
async function executeAsyncTask () {
|
||||
try {
|
||||
const valueA = await functionA();
|
||||
const valueB = await functionB(valueA);
|
||||
const valueC = await functionC(valueB);
|
||||
return await functionD(valueC);
|
||||
}
|
||||
catch(err) {
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Пример кода шаблона - обработка ошибок в стиле обратного вызова
|
||||
|
||||
```javascript
|
||||
getData(someParameter, function(err, result) {
|
||||
if(err !== null) {
|
||||
// do something like calling the given callback function and pass the error
|
||||
getMoreData(a, function(err, result) {
|
||||
if(err !== null) {
|
||||
// do something like calling the given callback function and pass the error
|
||||
getMoreData(b, function(c) {
|
||||
getMoreData(d, function(e) {
|
||||
if(err !== null ) {
|
||||
// you get the idea?
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Цитата из блога: "У нас проблема с обещаниями"
|
||||
|
||||
Из блога pouchdb.com
|
||||
|
||||
> … На самом деле, обратные вызовы делают что-то еще более зловещее: они лишают нас стека, что мы обычно принимаем как должное в языках программирования. Написание кода без стека очень похоже на вождение автомобиля без педали тормоза: вы не понимаете, насколько сильно оно вам нужно, пока не дойдете до него, а его там нет. Весь смысл обещаний состоит в том, чтобы вернуть нам языковые основы, которые мы потеряли при асинхронности: возврат, выброс и стек. Но вы должны знать, как правильно использовать обещания, чтобы воспользоваться ими.
|
||||
|
||||
### Цитата блога: "Метод обещаний гораздо более компактен"
|
||||
|
||||
Из блога gosquared.com
|
||||
|
||||
> … Метод обещаний гораздо компактнее, понятнее и быстрее для написания. Если ошибка или исключение происходят в любом из операций, они обрабатываются одним обработчиком .catch(). Наличие единого места для обработки всех ошибок означает, что вам не нужно писать проверку ошибок для каждого этапа работы.
|
||||
|
||||
### Цитата блога: "Обещания являются родными ES6, могут использоваться с генераторами"
|
||||
|
||||
Из блога StrongLoop
|
||||
|
||||
> … Обратные вызовы имеют паршивую историю обработки ошибок. Обещания лучше. Объедините встроенную обработку ошибок в Express с обещаниями и значительно снизьте вероятность возникновения необработанного исключения. Обещания являются родными ES6, могут использоваться с генераторами, а предложения ES7, такие как async/await, через компиляторы, такие как Babel.
|
||||
|
||||
### Цитата из блога: "Все те обычные конструкции управления потоком, к которым вы привыкли, полностью разрушены"
|
||||
|
||||
Из блога Бенно
|
||||
|
||||
> … Одно из лучших преимуществ асинхронного программирования на основе обратного вызова состоит в том, что в основном все эти обычные конструкции управления потоком, к которым вы привыкли, полностью разрушены. Тем не менее, я считаю, что больше всего разрушения коснулись обработки исключений. Javascript предоставляет довольно знакомую конструкцию try…catch для работы с исключениями. Проблема с исключениями состоит в том, что они обеспечивают отличный способ сокращения ошибок в стеке вызовов, но в конечном итоге оказываются совершенно бесполезными, если ошибка происходит в другом стеке…
|
||||
@ -0,0 +1,62 @@
|
||||
# Перехватывайте необработанные отказы от обещаний
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Как правило, большая часть современного кода приложения Node.js/Express выполняется в обещаниях - будь то в обработчике .then, обратном вызове функции или в блоке catch. Удивительно, но если разработчик не вспомнил о добавлении предложения .catch, ошибки, возникающие в этих местах, не обрабатываются обработчиком событий uncaughtException и исчезают. Недавние версии Node добавляли предупреждающее сообщение, когда появляется необработанный отказ, хотя это могло помочь заметить, когда что-то пойдет не так, но это явно не правильный метод обработки ошибок. Простое решение состоит в том, чтобы никогда не забывать добавлять предложения .catch в каждый вызов цепочки обещаний и перенаправлять их в централизованный обработчик ошибок. Однако построение вашей стратегии обработки ошибок только на основе дисциплины разработчика несколько хрупко. Следовательно, настоятельно рекомендуется использовать изящный запасной вариант и подписаться на `process.on('unhandledRejection', callback)` - это гарантирует, что любая ошибка обещания, если она не обрабатывается локально, будет обработана.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример кода: эти ошибки не будут обнаружены никаким обработчиком ошибок (кроме unhandledRejection)
|
||||
|
||||
```javascript
|
||||
DAL.getUserById(1).then((johnSnow) => {
|
||||
// this error will just vanish
|
||||
if(johnSnow.isAlive == false)
|
||||
throw new Error('ahhhh');
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример кода: отлов нерешенных и отклоненных обещаний
|
||||
|
||||
```javascript
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
// I just caught an unhandled promise rejection, since we already have fallback handler for unhandled errors (see below), let throw and let him handle that
|
||||
throw reason;
|
||||
});
|
||||
process.on('uncaughtException', (error) => {
|
||||
// I just received an error that was never handled, time to handle it and then decide whether a restart is needed
|
||||
errorManagement.handler.handleError(error);
|
||||
if (!errorManagement.handler.isTrustedError(error))
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Цитата в блоге: "Если вы можете сделать ошибку, в какой-то момент вы это сделаете"
|
||||
|
||||
Из блога Джеймса Нельсона
|
||||
|
||||
> Давайте проверим ваше понимание. Что из следующего вы ожидаете увидеть как ошибку в консоли?
|
||||
|
||||
```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 не будет печатать ошибки ни для одной из них. Проблема с тем, чтобы быть человеком, состоит в том, что если вы можете ошибиться, в какой-то момент вы это сделаете. Имея это в виду, кажется очевидным, что мы должны проектировать вещи таким образом, чтобы ошибки причиняли как можно меньше вреда, а это означает, что по умолчанию ошибки обрабатывают, а не отбрасывают их.
|
||||
83
sections/errorhandling/centralizedhandling.russian.md
Normal file
83
sections/errorhandling/centralizedhandling.russian.md
Normal file
@ -0,0 +1,83 @@
|
||||
# Обрабатывайте ошибки централизованно. Не в промежуточных слоях
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Без специального выделенного объекта для обработки ошибок больше шансов на то, что важные ошибки скрываются под радаром из-за неправильного обращения. Объект обработчика ошибок отвечает за отображение ошибки, например, путем записи в хорошо отформатированный регистратор, отправки событий в некоторые продукты мониторинга, такие как [Sentry](https://sentry.io/), [Rollbar](https://rollbar.com/) или [Raygun](https://raygun.com/). Большинство веб-фреймворков, таких как [Express](http://expressjs.com/en/guide/error-handling.html#writing-error-handlers), предоставляют механизм промежуточного программного обеспечения для обработки ошибок. Типичный поток обработки ошибок может быть следующим: какой-то модуль выдает ошибку -> маршрутизатор API перехватывает ошибку -> он передает ошибку промежуточному программному обеспечению (например, Express, KOA), которое отвечает за перехват ошибок -> вызывается централизованный обработчик ошибок -> промежуточному программному обеспечению сообщается, является ли эта ошибка ненадежной (не работающей), чтобы она могла корректно перезапустить приложение. Обратите внимание, что обычная, но неправильная практика - обрабатывать ошибки в промежуточном программном обеспечении Express - это не распространяется на ошибки, возникающие в не-веб-интерфейсах.
|
||||
|
||||
### Пример кода - типичный поток ошибок
|
||||
|
||||
```javascript
|
||||
// DAL layer, we don't handle errors here
|
||||
DB.addDocument(newCustomer, (error, result) => {
|
||||
if (error)
|
||||
throw new Error("Great error explanation comes here", other useful parameters)
|
||||
});
|
||||
|
||||
// API route code, we catch both sync and async errors and forward to the middleware
|
||||
try {
|
||||
customerService.addNew(req.body).then((result) => {
|
||||
res.status(200).json(result);
|
||||
}).catch((error) => {
|
||||
next(error)
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
// Error handling middleware, we delegate the handling to the centralized error handler
|
||||
app.use(async (err, req, res, next) => {
|
||||
const isOperationalError = await errorHandler.handleError(err);
|
||||
if (!isOperationalError) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Пример кода - обработка ошибок в выделенном объекте
|
||||
|
||||
```javascript
|
||||
module.exports.handler = new errorHandler();
|
||||
|
||||
function errorHandler() {
|
||||
this.handleError = async function(err) {
|
||||
await logger.logError(err);
|
||||
await sendMailToAdminIfCritical;
|
||||
await saveInOpsQueueIfCritical;
|
||||
await determineIfOperationalError;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Пример кода - антипаттерн: обработка ошибок в промежуточном программном обеспечении
|
||||
|
||||
```javascript
|
||||
// middleware handling the error directly, who will handle Cron jobs and testing errors?
|
||||
app.use((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, занимающий 1 место по ключевым словам "Обработка ошибок Node.js"
|
||||
|
||||
> … Вы можете обработать одну и ту же ошибку на нескольких уровнях стека. Это происходит, когда нижние уровни не могут сделать ничего полезного, кроме как передать ошибку вызывающей стороне, которая передает ошибку своей вызывающей стороне, и так далее. Зачастую только вызывающий пользователь верхнего уровня знает, что является подходящим ответом: попытка повторить операцию, сообщить пользователю об ошибке или что-то еще. Но это не значит, что вы должны пытаться сообщать обо всех ошибках в один обратный вызов верхнего уровня, потому что сам этот обратный вызов не может знать, в каком контексте произошла ошибка …
|
||||
|
||||
### Цитата из блога: "Обработка каждой ошибки по отдельности приведет к огромному дублированию"
|
||||
|
||||
Из блога JS Recipes, занимающий 17 место по ключевым словам "Обработка ошибок Node.js"
|
||||
|
||||
> … Только в контроллере api.js Hackathon Starter имеется более 79 случаев появления объектов ошибок. Обработка каждой ошибки в отдельности привела бы к огромному количеству дублирования кода. Следующее, что вы можете сделать, это делегировать всю логику обработки ошибок в промежуточное ПО Express …
|
||||
|
||||
### Цитата из блога: "В коде вашей базы данных нет места ошибкам HTTP"
|
||||
|
||||
Из блога Daily JS, занимающем 14 место по ключевым словам "Обработка ошибок Node.js"
|
||||
|
||||
> … Вы должны установить полезные свойства в объектах ошибок, но использовать их последовательно. И не пересекайте потоки: в коде вашей базы данных нет места ошибкам HTTP. Или для разработчиков браузеров, ошибки Ajax имеют место в коде, который общается с сервером, но не в коде, который обрабатывает шаблоны усов …
|
||||
52
sections/errorhandling/documentingusingswagger.russian.md
Normal file
52
sections/errorhandling/documentingusingswagger.russian.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Документироваие ошибок API при использовании Swagger или GraphQL
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
API-интерфейсы REST возвращают результаты с использованием кодов состояния HTTP, поэтому пользователь API должен знать не только о схеме API, но и о возможных ошибках - вызывающий может затем поймать ошибку и тактично ее обработать. Например, в документации по API может быть заранее указано, что HTTP-статус 409 возвращается, когда имя клиента уже существует (при условии, что API регистрирует новых пользователей), поэтому вызывающая сторона может соответственно отобразить лучший UX для данной ситуации. Swagger - это стандарт, определяющий схему документации API, предлагающую эко-систему инструментов, позволяющую легко создавать документацию в Интернете, см. экраны печати ниже.
|
||||
|
||||
Если вы уже приняли GraphQL для своих конечных точек API, ваша схема уже содержит строгие гарантии того, как должны выглядеть ошибки ([описано в спецификации](https://facebook.github.io/graphql/June2018/#sec-Errors )) и как они должны обрабатываться вашими инструментами на стороне клиента. Кроме того, вы также можете дополнить их документацией на основе комментариев.
|
||||
|
||||
### Пример ошибки GraphQL
|
||||
|
||||
> В этом примере используется [SWAPI](https://graphql.org/swapi-graphql), API-интерфейс Star Wars.
|
||||
|
||||
```graphql
|
||||
# should fail because id is not valid
|
||||
{
|
||||
film(id: "1ZmlsbXM6MQ==") {
|
||||
title
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"message": "No entry in local cache for https://swapi.co/api/films/.../",
|
||||
"locations": [
|
||||
{
|
||||
"line": 2,
|
||||
"column": 3
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
"film"
|
||||
]
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"film": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Цитата блога: "Вы должны сообщить своим абонентам, какие ошибки могут произойти"
|
||||
|
||||
Из блога Joyent, занял 1 место по ключевым словам "Node.js logging"
|
||||
|
||||
> Мы говорили о том, как обрабатывать ошибки, но когда вы пишете новую функцию, как вы доставляете ошибки в код, вызвавший вашу функцию? … Если вы не знаете, какие ошибки могут произойти, или не знаете, что они означают, то ваша программа может быть исправлена только случайно. Поэтому, если вы пишете новую функцию, вы должны сообщить своим абонентам, какие ошибки могут произойти и что они означают …
|
||||
|
||||
### Полезный инструмент: Swagger Online Documentation Creator
|
||||
|
||||

|
||||
47
sections/errorhandling/failfast.russian.md
Normal file
47
sections/errorhandling/failfast.russian.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Быстро проваливайтесь, проверяя аргументы, используя выделенную библиотеку
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Мы все знаем, как важно проверять аргументы и быстро отказывать, чтобы избежать скрытых ошибок (см. пример кода анти-паттерна ниже). Если нет, прочитайте о явном программировании и защитном программировании. В действительности, мы склонны избегать этого из-за досадного кодирования (например, проверка правильности иерархического объекта JSON с полями, такими как электронная почта и даты) - библиотеки, такие как Joi и Validator, превращают эту утомительную задачу в бриз.
|
||||
|
||||
### Википедия: защитное программирование
|
||||
|
||||
Защитное программирование - это подход к улучшению программного обеспечения и исходного кода, с точки зрения общего качества - уменьшения количества программных ошибок и проблем. Делать исходный код понятным - исходный код должен быть читаемым и понятным, чтобы он был одобрен в ходе аудита кода. Заставить программное обеспечение вести себя предсказуемо, несмотря на неожиданные входные данные или действия пользователя.
|
||||
|
||||
### Пример кода: проверка сложного ввода JSON с помощью "Joi"
|
||||
|
||||
```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
|
||||
// if the discount is positive let's then redirect the user to print his discount coupons
|
||||
function redirectToPrintDiscount(httpResponse, member, discount) {
|
||||
if (discount != 0) {
|
||||
httpResponse.redirect(`/discountPrintView/${member.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
redirectToPrintDiscount(httpResponse, someMember);
|
||||
// forgot to pass the parameter discount, why the heck was the user redirected to the discount screen?
|
||||
|
||||
```
|
||||
|
||||
### Цитата блога: "Вы должны немедленно выбросить эти ошибки"
|
||||
|
||||
Из блога: Джойент
|
||||
|
||||
> Вырожденный случай - это когда кто-то вызывает асинхронную функцию, но не передает обратный вызов. Вы должны немедленно выбросить эти ошибки, так как программа не работает, и наилучшая возможность отладки заключается в получении как минимум трассировки стека и, в идеале, файла ядра в точке ошибки. Для этого мы рекомендуем проверять типы всех аргументов в начале функции.
|
||||
17
sections/errorhandling/monitoring.russian.md
Normal file
17
sections/errorhandling/monitoring.russian.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Мониторинг
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
> На самом базовом уровне мониторинг означает, что вы можете *легко определить, когда на производстве происходят плохие вещи. Например, получая уведомления по электронной почте или Slack. Задача состоит в том, чтобы выбрать правильный набор инструментов, который удовлетворит ваши требования, не нарушая ваш банк. Позвольте мне начать с определения базового набора метрик, которые необходимо отслеживать для обеспечения работоспособного состояния - ЦП, ОЗУ сервера, ОЗУ процесса узла (менее 1,4 ГБ), количество ошибок в последнюю минуту, количество перезапусков процесса, среднее время ответа. Затем перейдите к некоторым дополнительным функциям, которые вам могут понравиться, и добавьте их в свой список пожеланий. Некоторые примеры функции мониторинга класса "люкс": профилирование БД, межсервисное измерение (то есть измерение бизнес-транзакций), интеграция с внешним интерфейсом, предоставление необработанных данных для пользовательских клиентов BI, уведомления Slack и многие другие.
|
||||
|
||||
Для реализации расширенных функций требуется длительная настройка или покупка коммерческого продукта, такого как Datadog, newrelic и тому подобное. К сожалению, достижение даже базовых знаний - это не прогулка в парке, поскольку некоторые метрики связаны с аппаратным обеспечением (ЦП), а другие живут в процессе узла (внутренние ошибки), поэтому все простые инструменты требуют некоторой дополнительной настройки. Например, решения для мониторинга поставщиков облачных вычислений (например, AWS CloudWatch, Google StackDriver) немедленно сообщат вам о метрике аппаратного обеспечения, но ничего не скажут о поведении внутреннего приложения. С другой стороны, в решениях на основе журналов, таких как ElasticSearch, по умолчанию отсутствует аппаратное представление. Решение состоит в том, чтобы дополнить ваш выбор отсутствующими метриками, например, популярным выбором является отправка журналов приложений в стек Elastic и настройка некоторого дополнительного агента (например, Beat) для обмена информацией, относящейся к оборудованию, для получения полной картины.
|
||||
|
||||
### Цитата из блога: "У нас проблема с обещаниями"
|
||||
|
||||
Из блога pouchdb.com, занимавший 11 место по ключевым словам "Узловые обещания"
|
||||
|
||||
> … Мы рекомендуем вам смотреть эти сигналы для всех ваших сервисов: Частота ошибок: потому что ошибки связаны с пользователем и сразу же влияют на ваших клиентов.
|
||||
Время ответа: потому что задержка напрямую влияет на ваших клиентов и бизнес.
|
||||
Пропускная способность: трафик помогает понять контекст повышенной частоты ошибок и задержки.
|
||||
Нагрузка: говорит о том, насколько "полон" ваш сервис. Если загрузка процессора составляет 90%, может ли ваша система обрабатывать больше трафика?
|
||||
…
|
||||
@ -0,0 +1,54 @@
|
||||
# Различайте операционные и программистские ошибки
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Различение следующих двух типов ошибок минимизирует время простоя вашего приложения и помогает избежать сумасшедших ошибок: Операционные ошибки относятся к ситуациям, когда вы понимаете, что произошло, и влияние этого - например, запрос к какой-либо службе HTTP не выполнен из-за проблемы с подключением. С другой стороны, ошибки программиста относятся к случаям, когда вы не знаете, почему, а иногда и откуда возникла ошибка - это может быть какой-то код, который пытался прочитать неопределенное значение или пул соединений с БД, который приводит к утечке памяти. Операционные ошибки относительно легко обрабатываются - обычно достаточно регистрации ошибки. Вещи становятся неприятными, когда появляется ошибка программиста, приложение может быть в несовместимом состоянии, и нет ничего лучше, чем перезапуск изящно.
|
||||
|
||||
### Пример кода - пометка ошибки как рабочей (доверенной)
|
||||
|
||||
```javascript
|
||||
// marking an error object as operational
|
||||
const myError = new Error("How can I add new product when no value provided?");
|
||||
myError.isOperational = true;
|
||||
|
||||
// or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
|
||||
class AppError {
|
||||
constructor (commonType, description, isOperational) {
|
||||
Error.call(this);
|
||||
Error.captureStackTrace(this);
|
||||
this.commonType = commonType;
|
||||
this.description = description;
|
||||
this.isOperational = isOperational;
|
||||
}
|
||||
};
|
||||
|
||||
throw new AppError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);
|
||||
|
||||
```
|
||||
|
||||
### Цитата блога: "Ошибки программиста - это ошибки в программе"
|
||||
|
||||
Из блога Джойент, занявшему 1 место по ключевым словам "Обработка ошибок Node.js"
|
||||
|
||||
> … Лучший способ исправить ошибки программиста - это сразу же аварийно завершить работу. Вы должны запустить свои программы, используя перезапуск, который автоматически перезапустит программу в случае сбоя. С перезапуском на месте сбой является самым быстрым способом восстановления надежного сервиса перед лицом временной ошибки программиста …
|
||||
|
||||
### Цитата из блога: "Нет безопасного способа уйти, не создав неопределенного хрупкого состояния"
|
||||
|
||||
Из официальной документации Node.js
|
||||
|
||||
> … По самой природе того, как throw работает в JavaScript, почти никогда не существует способа безопасно "выбрать то, на чем остановился", без утечки ссылок или создания какого-либо другого неопределенного хрупкого состояния. Самый безопасный способ отреагировать на возникшую ошибку - завершить процесс. Конечно, на обычном веб-сервере у вас может быть открыто много подключений, и нет смысла внезапно закрывать их, потому что кто-то еще вызвал ошибку. Лучший подход - отправить ответ об ошибке на запрос, который вызвал ошибку, позволяя остальным завершить работу в обычное время, и прекратить прослушивание новых запросов этого работника.
|
||||
|
||||
### Цитата блога: "В противном случае вы рискуете состоянием вашего приложения"
|
||||
|
||||
Из блога debugable.com, занявшего 3 место по ключевым словам "Node.js uncaught exception"
|
||||
|
||||
> … Итак, если вы действительно не знаете, что делаете, вам следует выполнить постепенный перезапуск службы после получения события исключения "uncaughtException". В противном случае вы рискуете привести к несогласованности состояния вашего приложения или сторонних библиотек, что приведет к всевозможным сумасшедшим ошибкам …
|
||||
|
||||
### Цитата блога: "Есть три школы мысли об обработке ошибок"
|
||||
|
||||
Из блога: JS Recipes
|
||||
|
||||
> … Существует три основных направления работы с ошибками:
|
||||
1. Дайте приложению упасть и перезапустите его.
|
||||
2. Обрабатывайте все возможные ошибоки и никогда не роняйте.
|
||||
3. Сбалансированный подход между двумя предыдущими.
|
||||
51
sections/errorhandling/shuttingtheprocess.russian.md
Normal file
51
sections/errorhandling/shuttingtheprocess.russian.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Изящно выходите из процесса, когда неизвестное случается
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Где-то в вашем коде объект-обработчик ошибок отвечает за принятие решения о том, как действовать при возникновении ошибки - если ошибка является доверенной (т.е. операционная ошибка, см. дальнейшее объяснение в этих практиках № 3), тогда записи в файл журнала может быть достаточно. Ситуация становится неясной, если ошибка незнакома - это означает, что какой-то компонент может быть в неисправном состоянии, и все будущие запросы могут быть сбойными. Например, если предположить, что единственная служба выдачи токенов с состоянием, которая вызвала исключение и потеряла свое состояние, отныне может вести себя неожиданно и вызывать сбой всех запросов. В этом сценарии завершите процесс и используйте "инструмент перезапуска" (например, Forever, PM2 и т.д.), Чтобы начать заново с чистым состоянием.
|
||||
|
||||
### Пример кода: решение о сбое
|
||||
|
||||
```javascript
|
||||
// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
|
||||
process.on('uncaughtException', function(error) {
|
||||
errorManagement.handler.handleError(error);
|
||||
if(!errorManagement.handler.isTrustedError(error))
|
||||
process.exit(1)
|
||||
});
|
||||
|
||||
// centralized error handler encapsulates error-handling related logic
|
||||
function errorHandler() {
|
||||
this.handleError = function (error) {
|
||||
return logger.logError(err)
|
||||
.then(sendMailToAdminIfCritical)
|
||||
.then(saveInOpsQueueIfCritical)
|
||||
.then(determineIfOperationalError);
|
||||
}
|
||||
|
||||
this.isTrustedError = function (error) {
|
||||
return error.isOperational;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Цитата из блога: "Лучший способ - это рухнуть"
|
||||
|
||||
Из блога Джойент
|
||||
|
||||
> … Лучший способ исправить ошибки программиста - это сразу же аварийно завершить работу. Вы должны запустить свои программы, используя перезапуск, который автоматически перезапустит программу в случае сбоя. С перезапуском на месте сбой является самым быстрым способом восстановления надежного сервиса перед лицом временной ошибки программиста …
|
||||
|
||||
### Цитата блога: "Есть три школы мысли об обработке ошибок"
|
||||
|
||||
Из блога: JS Recipes
|
||||
|
||||
> … Существует три основных направления работы с ошибками:
|
||||
1. Дайте приложению упасть и перезапустите его.
|
||||
2. Обрабатывайте все возможные ошибоки и никогда не роняйте.
|
||||
3. Сбалансированный подход между двумя предыдущими.
|
||||
|
||||
### Цитата из блога: "Нет безопасного способа уйти, не создав неопределенного хрупкого состояния"
|
||||
|
||||
Из официальной документации Node.js
|
||||
|
||||
> … По самой природе того, как throw работает в JavaScript, почти никогда не существует способа безопасно "выбрать то, на чем остановился", без утечки ссылок или создания какого-либо другого неопределенного хрупкого состояния. Самый безопасный способ отреагировать на возникшую ошибку - завершить процесс. Конечно, на обычном веб-сервере у вас может быть открыто много подключений, и нет смысла внезапно закрывать их, потому что кто-то еще вызвал ошибку. Лучший подход - отправить ответ об ошибке на запрос, который вызвал ошибку, позволяя остальным завершить работу в обычное время, и прекратить прослушивание новых запросов этого работника.
|
||||
38
sections/errorhandling/testingerrorflows.russian.md
Normal file
38
sections/errorhandling/testingerrorflows.russian.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Тестируйте потоки ошибок с использованием вашей любимой тестовой среды
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Тестирование "счастливых" путей не лучше, чем тестирование неудач. Хорошее тестирование покрытия кода требует тестирования исключительных путей. В противном случае, нет уверенности, что исключения действительно обрабатываются правильно. Каждая инфраструктура модульного тестирования, например [Mocha](https://mochajs.org/) и [Chai](http://chaijs.com/), поддерживает тестирование исключений (примеры кода ниже). Если вам кажется утомительным тестировать каждую внутреннюю функцию и исключение, вы можете согласиться с тестированием только ошибок HTTP REST API.
|
||||
|
||||
### Пример кода: обеспечение правильного исключения с помощью Mocha & Chai
|
||||
|
||||
```javascript
|
||||
describe("Facebook chat", () => {
|
||||
it("Notifies on new chat message", () => {
|
||||
var chatService = new chatService();
|
||||
chatService.participants = getDisconnectedParticipants();
|
||||
expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError);
|
||||
});
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Пример кода: застрахованное API возвращает правильный код ошибки HTTP
|
||||
|
||||
```javascript
|
||||
it("Creates new Facebook group", function (done) {
|
||||
var invalidGroupInfo = {};
|
||||
httpRequest({
|
||||
method: 'POST',
|
||||
uri: "facebook.com/api/groups",
|
||||
resolveWithFullResponse: true,
|
||||
body: invalidGroupInfo,
|
||||
json: true
|
||||
}).then((response) => {
|
||||
// if we were to execute the code in this block, no error was thrown in the operation above
|
||||
}).catch(function (response) {
|
||||
expect(400).to.equal(response.statusCode);
|
||||
done();
|
||||
});
|
||||
});
|
||||
```
|
||||
54
sections/errorhandling/usematurelogger.russian.md
Normal file
54
sections/errorhandling/usematurelogger.russian.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Используйте проверенный логгер, чтобы увеличить видимость ошибок
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
We all love console.log but obviously, a reputable and persistent logger like [Winston][winston], [Bunyan][bunyan] (highly popular) or [Pino][pino] (the new kid in town which is focused on performance) is mandatory for serious projects. A set of practices and tools will help to reason about errors much quicker – (1) log frequently using different levels (debug, info, error), (2) when logging, provide contextual information as JSON objects, see example below. (3) watch and filter logs using a log querying API (built-in in most loggers) or a log viewer software
|
||||
Мы все любим console.log, но, очевидно, авторитетный и постоянный регистратор, такой как [Winston][winston], [Bunyan][bunyan] (очень популярный) или [Pino][pino] (новый парень на районе, который ориентирован на производительность) является обязательным для серьезных проектов. Набор методов и инструментов поможет гораздо быстрее рассуждать об ошибках - (1) часто регистрировать с использованием разных уровней (отладка, информация, ошибка); (2) при ведении журнала, предоставлять контекстную информацию в виде объектов JSON, см. пример ниже; (3) просматривать и фильтровать журналы, используя API запросов журналов (встроенный в большинство регистраторов) или программу просмотра журналов; (4) разворачивать и составлять отчет для рабочей группы, используя инструменты предоставления оперативной информации, такие как Splunk.
|
||||
|
||||
[winston]: https://www.npmjs.com/package/winston
|
||||
[bunyan]: https://www.npmjs.com/package/bunyan
|
||||
[pino]: https://www.npmjs.com/package/pino
|
||||
|
||||
### Пример кода - Winston Logger в действии
|
||||
|
||||
```javascript
|
||||
// your centralized logger object
|
||||
var logger = new winston.Logger({
|
||||
level: 'info',
|
||||
transports: [
|
||||
new (winston.transports.Console)()
|
||||
]
|
||||
});
|
||||
|
||||
// custom code somewhere using the logger
|
||||
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });
|
||||
|
||||
```
|
||||
|
||||
### Пример кода - Запрос к папке журнала (поиск записей)
|
||||
|
||||
```javascript
|
||||
var options = {
|
||||
from: new Date - 24 * 60 * 60 * 1000,
|
||||
until: new Date,
|
||||
limit: 10,
|
||||
start: 0,
|
||||
order: 'desc',
|
||||
fields: ['message']
|
||||
};
|
||||
|
||||
|
||||
// Find items logged between today and yesterday.
|
||||
winston.query(options, function (err, results) {
|
||||
// execute callback with results
|
||||
});
|
||||
```
|
||||
|
||||
### Цитата блога: "Требования к логгеру"
|
||||
|
||||
Из блога Strong Loop
|
||||
|
||||
> Давайте определим несколько требований (для регистратора):
|
||||
1. Отметка времени каждой строки журнала. Это довольно очевидно - вы должны быть в состоянии сказать, когда произошла каждая запись в журнале.
|
||||
2. Формат записей должен быть легко усваиваемым людьми, а также машинами.
|
||||
3. Возможность для нескольких настраиваемых потоков назначения. Например, вы можете записывать журналы трассировки в один файл, но при возникновении ошибки запишите в тот же файл, затем в файл ошибок и отправьте электронное письмо одновременно …
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
### One Paragraph Explainer
|
||||
|
||||
The permissive nature of JS along with its variety code-flow options (e.g. EventEmitter, Callbacks, Promises, etc) pushes to great variance in how developers raise errors – some use strings, other define their own custom types. Using Node.js built-in Error object helps to keep uniformity within your code and with 3rd party libraries, it also preserves significant information like the StackTrace. When raising the exception, it’s usually a good practice to fill it with additional contextual properties like the error name and the associated HTTP error code. To achieve this uniformity and practices, consider extending the Error object with additional properties, see code example below
|
||||
The permissive nature of JavaScript along with its variety of code-flow options (e.g. EventEmitter, Callbacks, Promises, etc) pushes to great variance in how developers raise errors – some use strings, other define their own custom types. Using Node.js built-in Error object helps to keep uniformity within your code and with 3rd party libraries, it also preserves significant information like the StackTrace. When raising the exception, it’s usually a good practice to fill it with additional contextual properties like the error name and the associated HTTP error code. To achieve this uniformity and practices, consider extending the Error object with additional properties, see code example below
|
||||
|
||||
### Code Example – doing it right
|
||||
|
||||
|
||||
81
sections/errorhandling/useonlythebuiltinerror.russian.md
Normal file
81
sections/errorhandling/useonlythebuiltinerror.russian.md
Normal file
@ -0,0 +1,81 @@
|
||||
# Используйте только встроенный объект Error
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Лозволяющая природа JavaScript наряду с его разнообразием параметров потока кода (например, EventEmitter, Callbacks, Promises и т.д.) приводит к значительному расхождению в том, как разработчики выдают ошибки - некоторые используют строки, другие определяют свои собственные пользовательские типы. Использование встроенного объекта Error в Node.js помогает сохранить единообразие в вашем коде, а с помощью сторонних библиотек он также сохраняет важную информацию, такую как StackTrace. При возникновении исключения обычно рекомендуется заполнить его дополнительными контекстными свойствами, такими как имя ошибки и связанный код ошибки HTTP. Чтобы добиться этого единообразия и практики, рассмотрите возможность расширения объекта Error дополнительными свойствами, см. пример кода ниже.
|
||||
|
||||
### Пример кода - делай все правильно
|
||||
|
||||
```javascript
|
||||
// throwing an Error from typical function, whether sync or async
|
||||
if(!productToAdd)
|
||||
throw new Error("How can I add new product when no value provided?");
|
||||
|
||||
// 'throwing' an Error from EventEmitter
|
||||
const myEmitter = new MyEmitter();
|
||||
myEmitter.emit('error', new Error('whoops!'));
|
||||
|
||||
// 'throwing' an Error from a Promise
|
||||
const addProduct = async (productToAdd) => {
|
||||
try {
|
||||
const existingProduct = await DAL.getProduct(productToAdd.id);
|
||||
if (existingProduct !== null) {
|
||||
throw new Error("Product already exists!");
|
||||
}
|
||||
} catch (err) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Пример кода - антипаттерн
|
||||
|
||||
```javascript
|
||||
// throwing a string lacks any stack trace information and other important data properties
|
||||
if(!productToAdd)
|
||||
throw ("How can I add new product when no value provided?");
|
||||
```
|
||||
|
||||
### Пример кода - делаем это еще лучше
|
||||
|
||||
```javascript
|
||||
// centralized error object that derives from Node’s Error
|
||||
function AppError(name, httpCode, description, isOperational) {
|
||||
Error.call(this);
|
||||
Error.captureStackTrace(this);
|
||||
this.name = name;
|
||||
//...other properties assigned here
|
||||
};
|
||||
|
||||
AppError.prototype.__proto__ = Error.prototype;
|
||||
|
||||
module.exports.AppError = AppError;
|
||||
|
||||
// client throwing an exception
|
||||
if(user == null)
|
||||
throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, "further explanation", true)
|
||||
```
|
||||
|
||||
### Цитата блога: "Я не вижу смысла в том, чтобы иметь много разных типов"
|
||||
|
||||
Из блога Бен Надель, занял 5 место по ключевым словам "объект ошибки Node.js"
|
||||
|
||||
> … Лично я не вижу смысла в том, чтобы иметь множество различных типов объектов ошибок - JavaScript, как язык, похоже, не предназначен для поиска ошибок на основе конструктора. Таким образом, дифференцирование по свойству объекта кажется гораздо проще, чем по типу Constructor …
|
||||
|
||||
### Цитата блога: "Строка не является ошибкой"
|
||||
|
||||
Из блога devthought.com, занял 6 место по ключевым словам "Объект ошибки Node.js"
|
||||
|
||||
> … передача строки вместо ошибки приводит к снижению совместимости между модулями. Он нарушает контракты с API, которые могут выполнять проверки ошибок instanceof или хотят узнать больше об ошибке. Объекты ошибок, как мы увидим, обладают очень интересными свойствами в современных механизмах JavaScript, помимо хранения сообщения, переданного конструктору …
|
||||
|
||||
### Цитата из блога: "Наследование от ошибки не увеличивает ценность"
|
||||
|
||||
Из блога Machadogj
|
||||
|
||||
> … Одна проблема, которую я имею с классом Error, заключается в том, что его не так просто расширить. Конечно, вы можете наследовать класс и создавать свои собственные классы ошибок, такие как HttpError, DbError и т.д. Однако это занимает время и не добавляет слишком много значения, если вы не делаете что-то с типами. Иногда вам просто нужно добавить сообщение и сохранить внутреннюю ошибку, а иногда вам может понадобиться расширить ошибку с помощью параметров, и так далее …
|
||||
|
||||
### Цитата из блога: "Все ошибки JavaScript и системы, возникающие в Node.js, наследуются от ошибок"
|
||||
|
||||
Из официальной документации Node.js
|
||||
|
||||
> … Все ошибки JavaScript и System, возникающие в Node.js, наследуются или являются экземплярами стандартного класса JavaScript Error и гарантированно предоставляют как минимум свойства, доступные в этом классе. Общий объект JavaScript Error, который не обозначает каких-либо конкретных обстоятельств, по которым произошла ошибка. Объекты ошибок фиксируют "трассировку стека", детализирующую точку в коде, в которой был создан экземпляр ошибки, и могут предоставлять текстовое описание ошибки. Все ошибки, сгенерированные Node.js, включая все системные ошибки и ошибки JavaScript, будут либо экземплярами класса Error, либо наследоваться от него …
|
||||
70
sections/performance/nativeoverutil.russian.md
Normal file
70
sections/performance/nativeoverutil.russian.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Предпочитайте нативные методы JS над пользовательскими утилитами, такими как Lodash
|
||||
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Иногда использовать нативные методы лучше, чем привязывать `lodash` или `underscore`, потому что это не приведет к повышению производительности и использованию большего пространства, чем необходимо.
|
||||
Результативность с использованием собственных методов приводит к [общему увеличению ~ 50%](https://github.com/Berkmann18/NativeVsUtils/blob/master/analysis.xlsx), который включает следующие методы: `Array.concat`, `Array.fill`, `Array.filter`, `Array.map`, `(Array|String).indexOf`, `Object.find`, ...
|
||||
|
||||
|
||||
<!-- comp here: https://gist.github.com/Berkmann18/3a99f308d58535ab0719ac8fc3c3b8bb-->
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример: сравнение производительности - Lodash vs V8 (Native)
|
||||
На приведенном ниже графике показано [среднее из эталонных показателей для различных методов Lodash](https://github.com/Berkmann18/NativeVsUtils/blob/master/nativeVsLodash.ods), это показывает, что методы Lodash занимают в среднем на 146,23% больше время для выполнения тех же задач, что и методы V8.
|
||||
|
||||

|
||||
|
||||
### Пример кода - бенчмарк-тест для `_.concat`/`Array.concat`
|
||||
```javascript
|
||||
const _ = require('lodash'),
|
||||
__ = require('underscore'),
|
||||
Suite = require('benchmark').Suite,
|
||||
opts = require('./utils'); //cf. https://github.com/Berkmann18/NativeVsUtils/blob/master/utils.js
|
||||
|
||||
const concatSuite = new Suite('concat', opts);
|
||||
const array = [0, 1, 2];
|
||||
|
||||
concatSuite.add('lodash', () => _.concat(array, 3, 4, 5))
|
||||
.add('underscore', () => __.concat(array, 3, 4, 5))
|
||||
.add('native', () => array.concat(3, 4, 5))
|
||||
.run({ 'async': true });
|
||||
```
|
||||
|
||||
Который возвращает это:
|
||||
|
||||

|
||||
|
||||
Вы можете найти больший список тестов [здесь](https://github.com/Berkmann18/NativeVsUtils/blob/master/index.txt) или альтернативно [запустить это](https://github.com/Berkmann18/NativeVsUtils/blob/master/index.js), который показывает то же самое, но в цвете.
|
||||
|
||||
### Цитата блога: "Вам не нужно (не нужно) Lodash/Underscore"
|
||||
|
||||
Из [репо по этому вопросу, в котором основное внимание уделяется Lodash и Underscore](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore).
|
||||
|
||||
> Lodash и Underscore - отличные современные библиотеки утилит JavaScript, и они широко используются разработчиками Front-end. Однако, когда вы ориентируетесь на современные браузеры, вы можете обнаружить, что есть много методов, которые уже изначально поддерживаются благодаря ECMAScript5 [ES5] и ECMAScript2015 [ES6]. Если вы хотите, чтобы вашему проекту требовалось меньше зависимостей, и вы четко знаете целевой браузер, то вам может не потребоваться Lodash/Underscore.
|
||||
|
||||
### Пример: Linting для использования неродных методов
|
||||
Существует [плагин ESLint](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore), который определяет, где вы используете библиотеки, но не должен предупреждать вас с предложениями (см. пример ниже).<br>
|
||||
Вы можете настроить его, добавив плагин `eslint-plugin-you-dont-need-lodash-underscore` в файл конфигурации ESLint:
|
||||
```json
|
||||
{
|
||||
"extends": [
|
||||
"plugin:you-dont-need-lodash-underscore/compatible"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Пример: обнаружение использования утилит не-v8 с использованием линтера
|
||||
Рассмотрим файл ниже:
|
||||
```js
|
||||
const _ = require('lodash');
|
||||
// ESLint will flag the line above with a suggestion
|
||||
console.log(_.map([0, 1, 2, 4, 8, 16], x => `d${x}`));
|
||||
```
|
||||
Вот что ESLint будет выводить при использовании плагина YDNLU.
|
||||

|
||||
|
||||
Конечно, приведенный выше пример не выглядит реалистичным, если учесть, какие будут исходные кодовые базы, но вы поняли идею.
|
||||
25
sections/production/apmproducts.russian.md
Normal file
25
sections/production/apmproducts.russian.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Уверенный пользовательский опыт с продуктами APM
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
APM (мониторинг производительности приложений) относится к семейству продуктов, целью которых является мониторинг производительности приложений от конца до конца, в том числе с точки зрения клиента. В то время как традиционные решения мониторинга ориентированы на исключения и автономные технические метрики (например, отслеживание ошибок, медленные конечные точки сервера и т.д.), В реальном мире наше приложение может создавать разочарованных пользователей без каких-либо исключений кода, например, если какая-то служба промежуточного программного обеспечения работает очень медленно. Продукты APM измеряют пользовательский опыт от начала до конца, например, с учетом системы, которая включает интерфейсный интерфейс пользователя и несколько распределенных сервисов - некоторые продукты APM могут определить, как быстро выполняется транзакция, охватывающая несколько уровней. Это может сказать, является ли пользовательский опыт твердым и указывает на проблему. Это привлекательное предложение имеет относительно высокую цену, поэтому оно рекомендуется для крупномасштабных и сложных продуктов, которые требуют выхода за рамки простого мониторинга.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример APM - коммерческий продукт, который визуализирует производительность кросс-сервисного приложения
|
||||
|
||||

|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример APM - коммерческий продукт, который подчеркивает оценку пользовательского опыта
|
||||
|
||||

|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример APM - коммерческий продукт, который выделяет медленные пути кода
|
||||
|
||||

|
||||
38
sections/production/assigntransactionid.russian.md
Normal file
38
sections/production/assigntransactionid.russian.md
Normal file
@ -0,0 +1,38 @@
|
||||
# Назначьте "TransactionId" для каждого вхождения журнала логирования
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Типичный журнал - это хранилище записей всех компонентов и запросов. При обнаружении какой-либо подозрительной линии или ошибки становится трудно сопоставить другие строки, принадлежащие к тому же конкретному потоку (например, пользователь "Джон" пытался что-то купить). Это становится еще более критическим и сложным в микросервисной среде, когда запрос/транзакция может охватывать несколько компьютеров. Чтобы решить эту проблему, присвойте уникальное значение идентификатора транзакции всем записям одного и того же запроса, чтобы при обнаружении одной строки можно было копировать идентификатор и искать каждую строку, имеющую аналогичный идентификатор транзакции. Однако добиться этого в узле непросто, так как для обслуживания всех запросов используется один поток - рассмотрите возможность использования библиотеки, которая может группировать данные на уровне запроса - см. Пример кода на следующем слайде. При вызове другого микросервиса передайте идентификатор транзакции, используя заголовок HTTP, например "x-transaction-id", чтобы сохранить тот же контекст.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример кода: типичная экспресс-конфигурация
|
||||
|
||||
```javascript
|
||||
// when receiving a new request, start a new isolated context and set a transaction Id. The following example is using the npm library continuation-local-storage to isolate requests
|
||||
|
||||
const { createNamespace } = require('continuation-local-storage');
|
||||
var session = createNamespace('my session');
|
||||
|
||||
router.get('/:id', (req, res, next) => {
|
||||
session.set('transactionId', 'some unique GUID');
|
||||
someService.getById(req.params.id);
|
||||
logger.info('Starting now to get something by Id');
|
||||
});
|
||||
|
||||
// Now any other service or components can have access to the contextual, per-request, data
|
||||
class someService {
|
||||
getById(id) {
|
||||
logger.info(“Starting to get something by Id”);
|
||||
// other logic comes here
|
||||
}
|
||||
}
|
||||
|
||||
// The logger can now append the transaction-id to each entry so that entries from the same request will have the same value
|
||||
class logger {
|
||||
info (message)
|
||||
{console.log(`${message} ${session.get('transactionId')}`);}
|
||||
}
|
||||
```
|
||||
42
sections/production/bestateless.russian.md
Normal file
42
sections/production/bestateless.russian.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Не прописывайтесь на постоянку, убивайте свои серверы почти каждый день
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Сталкивались ли вы когда-нибудь с серьезной производственной проблемой, когда на одном сервере отсутствовала какая-то часть конфигурации или данных? Вероятно, это связано с некоторой ненужной зависимостью от локального актива, который не является частью развертывания. Многие успешные продукты относятся к серверам как к птице фениксу - она умирает и периодически перерождается без какого-либо ущерба. Другими словами, сервер - это просто аппаратное обеспечение, которое некоторое время выполняет ваш код и заменяется после этого.
|
||||
Этот подход
|
||||
|
||||
- позволяет масштабировать, динамически добавляя и удаляя серверы без каких-либо побочных эффектов.
|
||||
- упрощает обслуживание, поскольку освобождает наш разум от оценки состояния каждого сервера.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример кода: антипаттерны
|
||||
|
||||
```javascript
|
||||
// Typical mistake 1: saving uploaded files locally on a server
|
||||
var multer = require('multer'); // express middleware for handling multipart uploads
|
||||
var upload = multer({ dest: 'uploads/' });
|
||||
|
||||
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {});
|
||||
|
||||
// Typical mistake 2: storing authentication sessions (passport) in a local file or memory
|
||||
var FileStore = require('session-file-store')(session);
|
||||
app.use(session({
|
||||
store: new FileStore(options),
|
||||
secret: 'keyboard cat'
|
||||
}));
|
||||
|
||||
// Typical mistake 3: storing information on the global object
|
||||
Global.someCacheLike.result = { somedata };
|
||||
```
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Что говорят другие блоггеры
|
||||
|
||||
Из блога [Мартин Фаулер](https://martinfowler.com/bliki/PhoenixServer.html):
|
||||
> ... Однажды у меня появилась эта фантазия о запуске службы сертификации для операций. Оценка сертификации будет состоять из того, что мы с коллегой поедем в корпоративный центр обработки данных и расскажем о важнейших производственных серверах с бейсбольной битой, бензопилой и водяным пистолетом. Оценка будет основываться на том, сколько времени потребуется оперативной группе для того, чтобы все приложения снова заработали и снова заработали. Это может быть глупой фантазией, но здесь есть кусочек мудрости. В то время как вы должны отказаться от бейсбольных бит, это хорошая идея, чтобы виртуально сжигать ваши серверы через регулярные промежутки времени. Сервер должен быть как феникс, регулярно восстающий из пепла ...
|
||||
|
||||
<br/><br/>
|
||||
45
sections/production/createmaintenanceendpoint.russian.md
Normal file
45
sections/production/createmaintenanceendpoint.russian.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Создавайте конечную точку обслуживания
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Конечная точка обслуживания - это высокозащищенный HTTP API, который является частью кода приложения, и его назначение - использовать команду ops/production для мониторинга и предоставления функциональных возможностей обслуживания. Например, он может вернуть дамп кучи (моментальный снимок памяти) процесса, сообщить о наличии утечек памяти и даже разрешить выполнение команд REPL напрямую. Эта конечная точка необходима, когда обычные инструменты DevOps (продукты мониторинга, журналы и т.д.) не в состоянии собрать какой-то конкретный тип информации или вы решили не покупать/устанавливать такие инструменты. Золотое правило заключается в использовании профессиональных и внешних инструментов для мониторинга и обслуживания производства, обычно они более надежны и точны. Тем не менее, могут быть случаи, когда универсальные инструменты не смогут извлечь информацию, относящуюся к Node или вашему приложению - например, если вы захотите сгенерировать снимок памяти в момент завершения цикла GC - несколько библиотек npm буду рады сделать это за вас, но популярные инструменты мониторинга, скорее всего, упустят эту функцию. Важно сохранять эту конечную точку частной и доступной только для администраторов, поскольку она может стать целью атаки DDOS.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример кода: создание дампа кучи с помощью кода
|
||||
|
||||
```javascript
|
||||
const heapdump = require('heapdump');
|
||||
|
||||
// Check if request is authorized
|
||||
function isAuthorized(req) {
|
||||
// ...
|
||||
}
|
||||
|
||||
router.get('/ops/heapdump', (req, res, next) => {
|
||||
if (!isAuthorized(req)) {
|
||||
return res.status(403).send('You are not authorized!');
|
||||
}
|
||||
|
||||
logger.info('About to generate heapdump');
|
||||
|
||||
heapdump.writeSnapshot((err, filename) => {
|
||||
console.log('heapdump file is ready to be sent to the caller', filename);
|
||||
fs.readFile(filename, "utf-8", (err, data) => {
|
||||
res.end(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Рекомендуемые ресурсы
|
||||
|
||||
[Подготовка вашего приложения Node.js к производственому запуску (слайды)](http://naugtur.pl/pres3/node2prod)
|
||||
|
||||
▶ [Подготовка вашего приложения Node.js к производственому запуску (видео)](https://www.youtube.com/watch?v=lUsNne-_VIk)
|
||||
|
||||

|
||||
51
sections/production/delegatetoproxy.russian.md
Normal file
51
sections/production/delegatetoproxy.russian.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Делегируйте все возможное (например, gzip, SSL) обратному прокси
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Объяснение в один абзац
|
||||
|
||||
Это очень заманчиво для карго-культа Express и использования его богатого промежуточного программного обеспечения для задач, связанных с сетью, таких как обслуживание статических файлов, кодирование gzip, запросы регулирования, завершение SSL и т.д. Это снижение производительности из-за его однопоточной модели, которая сохранит процессор занят для длительных периодов (помните, что модель выполнения Node оптимизирована для коротких задач или задач, связанных с асинхронным вводом-выводом). Лучшим подходом является использование инструмента, обладающего опытом в сетевых задачах. Наиболее популярными являются nginx и HAproxy, которые также используются крупнейшими поставщиками облачных технологий для снижения нагрузки на входящие процессы Node.js.
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Пример конфигурации nginx - Использование nginx для сжатия ответов сервера
|
||||
|
||||
```nginx
|
||||
# configure gzip compression
|
||||
gzip on;
|
||||
gzip_comp_level 6;
|
||||
gzip_vary on;
|
||||
|
||||
# configure upstream
|
||||
upstream myApplication {
|
||||
server 127.0.0.1:3000;
|
||||
server 127.0.0.1:3001;
|
||||
keepalive 64;
|
||||
}
|
||||
|
||||
#defining web server
|
||||
server {
|
||||
# configure server with ssl and error pages
|
||||
listen 80;
|
||||
listen 443 ssl;
|
||||
ssl_certificate /some/location/sillyfacesociety.com.bundle.crt;
|
||||
error_page 502 /errors/502.html;
|
||||
|
||||
# handling static content
|
||||
location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) {
|
||||
root /usr/local/silly_face_society/node/public;
|
||||
access_log off;
|
||||
expires max;
|
||||
}
|
||||
```
|
||||
|
||||
<br/><br/>
|
||||
|
||||
### Что говорят другие блоггеры
|
||||
|
||||
* Из блога [Mubaloo](http://mubaloo.com/best-practices-deploying-node-js-applications):
|
||||
> … Попасть в эту ловушку очень легко - вы видите пакет типа "Экспресс" и думаете: "Круто! Давайте начнем", - вы кодируете, и у вас есть приложение, которое делает то, что вы хотите. Это отлично, и, честно говоря, вы выиграли много битвы. Однако вы проиграете войну, если загрузите свое приложение на сервер и прослушаете его на своем HTTP-порте, потому что вы забыли очень важную вещь: Node не является веб-сервером. **Как только любой объем трафика начнет попадать в ваше приложение, вы заметите, что что-то начинает работать неправильно: соединения теряются, ресурсы перестают обслуживаться, или, в худшем случае, происходит сбой вашего сервера. То, что вы делаете, это пытаетесь заставить Node справиться со всеми сложными вещами, которые проверенный веб-сервер делает действительно хорошо. Зачем изобретать велосипед?**
|
||||
> **Это только для одного запроса, для одного изображения и с учетом того, что это память, которую ваше приложение может использовать для важных вещей, таких как чтение базы данных или обработка сложной логики; зачем вам калечить ваше приложение ради удобства?**
|
||||
|
||||
* Из блога [Argteam](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load):
|
||||
> Несмотря на то, что в express.js есть встроенная статическая обработка файлов через некоторое промежуточное ПО для подключения, вы никогда не должны его использовать. **Nginx может гораздо лучше справляться со статическими файлами и предотвращать засорение запросов на не динамический контент нашими процессами узлов** …
|
||||
Reference in New Issue
Block a user