• Nodejs错误处理详细指南


    Nodejs错误处理详细指南

    学习 Node.js 中的高级错误处理技术,以增强应用程序的可靠性和稳定性。

    Node.js 中,我们可以使用各种技术和方法来处理错误,可以查看这篇文章。错误处理是任何 Node.js 应用程序的一个重要方面。正确管理错误可以显着提高代码库的稳定性、可靠性和可维护性。在这篇文章中,我们将探索 Node.js 中错误处理的各种最佳实践和技术,使我们能够像经验丰富的 Node.js 开发人员一样处理错误。

    我们可以在 Node.js 中采用多种高级错误处理技术。这些技术允许对错误处理进行更精细的控制,增强错误报告并提高代码可维护性。以下是 Node.js 中的一些高级错误处理技术:

    1. 错误中间件
    2. 错误处理框架
    3. 错误冒泡
    4. 优雅关机
    5. 错误监控和报告
    6. 断路器模式
    7. 背压处理
    8. 自动重试
    9. 日志记录和分析

    1. 错误中间件

    Express 等框架中的错误中间件功能允许我们处理请求处理期间发生的错误。当错误传递到 next方法时,将执行这些错误处理函数。以下是逐步细分的步骤:

    步骤1:定义错误中间件函数,有四个参数:errreqresnext

    app.use((err, req, res, next) => {
      // Handle the error
    });
    
    • 1
    • 2
    • 3

    步骤2:在错误中间件函数内处理错误。我们可以访问错误消息、状态代码和其他相关详细信息,以制定适当的错误响应。

    app.use((err , req, res, next ) => { //
      处理错误
      res.status (err.status || 500 ).json({ error : err.message }); 
    });
    
    • 1
    • 2
    • 3
    • 4

    示例1

    app.get('/users/:id', (req, res, next) => {
      const userId = req.params.id;
    if (userId === 'admin') {
        const error = new Error('Access denied');
        error.status = 403;
        next(error);
      } else {
        // Handle the regular flow
      }
    });
    app.use((err, req, res, next) => {
      res.status(err.status || 500).json({ error: err.message });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在此示例中,当请求 IDadmin 的用户时,我们创建一个自定义错误对象并将其传递给next()。 错误中间件函数捕获错误并发送正确的 JSON 响应,状态代码为 403(禁止)。

    示例2

    app.get('/data', async (req, res, next) => {
      try {
        const data = await fetchData();
        res.json(data);
      } catch (error) {
        next(error);
      }
    });
    
    app.use((err, req, res, next) => {
      res.status(err.status || 500).json({ error: err.message });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在此示例中,异步路由处理程序获取数据。如果异步操作期间发生错误,则使用 try-catch 块捕获错误,并将错误传递给next()。 错误中间件函数处理错误并发送适当的响应。

    2. 错误处理框架

    Node.js 生态系统中有多个错误处理框架和库提供了高级错误处理功能。我们现在只介绍两个比较流行的例子:BoomYouch

    示例1:使用Boom

    const express = require('express');
    const boom = require('@hapi/boom');
    
    const app = express();
    app.get('/users/:id', (req, res, next) => {
      const userId = req.params.id;
      if (userId === 'admin') {
        next(boom.forbidden('Access denied'));
      } else {
        // ...
      }
    });
    app.use((err, req, res, next) => {
      if (!err.isBoom) {
        next(err);
        return;
      }
      const { output } = err;
      res.status(output.statusCode).json({ error: output.payload });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在此示例中,使用boom.forbidden()方法创建拒绝访问错误。当用户ID是admin时,错误被传递到错误中间件函数。错误中间件检查错误是否为 Boom 错误并发送格式化的 JSON 响应。

    示例2:使用Youch

    const express = require('express');
    const youch = require('youch');
    
    const app = express();
    app.get('/users/:id', (req, res, next) => {
      const userId = req.params.id;
      if (userId === 'admin') {
        const error = new Error('Access denied');
        next(error);
      } else {
        // ...
      }
    });
    app.use((err, req, res, next) => {
      const youchInstance = new youch(err, req);
      youchInstance.toJSON().then((errorJSON) => {
        res.status(500).json(errorJSON);
      });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. 错误冒泡

    正确的错误冒泡可确保在应用程序中适当的地方捕获和处理错误。可以通过将错误抛出到同步代码中或在异步代码中返回被拒绝的 Promise 来冒泡。让我们进一步探讨这个话题:

    抛出错误(同步)

    在同步代码中,我们可以使用throw语句抛出一个错误。错误将在调用堆栈中向上冒泡,直到被 try-catch 块捕获。

    function divide(a, b) {
      if (b === 0) {
        throw new Error('Cannot divide by zero');
      }
      return a / b;
    }
    
    try {
      const result = divide(10, 0);
      console.log(result);
    } catch (error) {
      console.error('An error occurred:', error);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在此示例中,divide函数在尝试除以零时抛出错误。该错误在try-catch 代码块中捕获并进行相应的处理。

    Promise.reject(异步)

    在使用 Promise 的异步代码中,可以通过使用Promise.reject()方法返回被拒绝的 Promise 来抛出错误。

    function fetchData() {
      return new Promise((resolve, reject) => {
        // Simulating an asynchronous operation
        setTimeout(() => {
          reject(new Error('Data fetch failed'));
        }, 2000);
      });
    }
    
    fetchData()
      .then((data) => {
        console.log(data);
      })
      .catch((error) => {
        console.error('An error occurred:', error);
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在此示例中,fetchData函数返回一个 PromisePromise 在延迟后被故意拒绝。使用Promise 链上的catch方法捕获错误并进行相应处理。

    4. 优雅关机

    在应用程序中实现优雅的关闭机制允许我们在关闭过程中处理错误。这涉及在终止应用程序之前正确关闭数据库连接、释放资源以及通知外部服务。让我们看看一些示例:

    示例1:处理终止信号

    侦听 SIGINT (Ctrl+C) 和 SIGTERM 等终止信号,并在退出应用程序之前执行清理任务。

    process.on('SIGINT', () => {
      // 退出控制台前清理任务
      console.log('优雅关闭...');
      // 关闭数据库连接等操作
      process.exit(0); // 退出
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例2:注册清理处理程序

    注册清理处理程序以确保即使在意外错误情况下也能正确清理。

    process.on('uncaughtException', (error) => {
      console.error('Uncaught Exception:', error);
      process.exit(1); // Exit with error code
    });
    
    process.on('unhandledRejection', (reason, promise) => {
      console.error('Unhandled Promise Rejection:', reason);
      process.exit(1); // Exit with error code
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在此示例中,我们处理未捕获的异常和未处理的承诺拒绝,并在退出应用程序之前执行清理任务。

    5. 错误监控和报告

    将错误监控和报告工具集成到我们的应用程序中有助于跟踪、分析和报告生产环境中发生的错误。让我们看看所涉及的步骤:

    第 1 步:选择错误监控服务

    选择错误监控服务/工具,例如 SentryBugsnagRollbar

    步骤2:安装并配置错误监控服务

    为所选服务安装错误监控库,并使用我们的 API 密钥或特定于项目的设置对其进行配置。

    第 3 步:将错误监控集成到应用程序中

    const express = require('express');
    const app = express();
    
    // Error monitoring library initialization
    const Sentry = require('@sentry/node');
    Sentry.init({ dsn: 'YOUR_DSN_HERE' });
    app.get('/', (req, res) => {
      // Some code that may throw an error
      throw new Error('Error occurred!');
    });
    // Error handler middleware
    app.use((err, req, res, next) => {
      Sentry.captureException(err); // Report the error to Sentry
      res.status(500).json({ error: 'Something went wrong' });
    });
    app.listen(3000, () => {
      console.log('Server started on port 3000');
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在本例中,我们使用Sentry错误监控服务。我们使用提供的 DSN(数据源名称)对其进行初始化,并在错误处理中间件中使用Sentry.captureException()捕获异常。

    6. 断路器模式

    断路器模式有助于管理故障并防止分布式系统中的级联故障。通过将远程服务调用包装在断路器中,我们可以处理错误、提供回退响应并避免系统不堪重负。让我们看看所涉及的步骤:

    第 1 步:选择断路器库

    选择类似opossumbrakes 的断路器库来实现断路器模式。

    第 2 步:包装远程服务调用

    将远程服务调用包装在断路器中以处理错误并提供回退响应。

    const CircuitBreaker = require('opossum');
    
    const options = {
      errorThresholdPercentage: 50,
      timeout: 3000,
      resetTimeout: 30000,
    };
    const breaker = new CircuitBreaker(remoteServiceCall, options);
    breaker.fallback(() => {
      return 'Fallback response';
    });
    app.get('/', (req, res) => {
      breaker.fire()
        .then((result) => {
          res.json(result);
        })
        .catch((error) => {
          res.status(500).json({ error: 'Service unavailable' });
        });
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在此示例中,我们使用opossum库来实现断路器模式。我们在进行远程服务调用时定义回退响应并处理错误。

    7. 背压处理

    在处理大容量流或异步操作时,背压处理至关重要。实施背压机制可以防止过载并实现更好的错误处理。以下是涉及的步骤:

    第 1 步:使用背压感知流

    利用背压感知流,例如Node.js 中的ReadStreamWriteStream这些API来实现自动处理流量控制。

    第 2 步:暂停和恢复流

    暂停和恢复流以控制数据流。当可写流尚未准备好处理数据时,暂停可读流。

    const fs = require('fs');
    
    const readableStream = fs.createReadStream('largeFile.txt');
    const writableStream = fs.createWriteStream('output.txt');
    writableStream.on('drain', () => {
      readableStream.resume();
    });
    readableStream.on('data', (chunk) => {
      if (!writableStream.write(chunk)) {
        readableStream.pause();
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在此示例中,可读流从大文件中读取数据,可写流将数据写入输出文件。

    8. 自动重试

    自动重试是一种使用指数退避策略自动重试失败操作(例如网络请求)的技术。它有助于处理暂时性错误并自动恢复。让我们看看所涉及的步骤:

    第 1 步:选择重试库

    选择类似async-retry的重试库或实现我们自己的重试逻辑。

    第 2 步:定义重试选项

    定义重试选项,包括重试尝试次数、退避策略和重试条件。

    const retry = require('async-retry');
    
    const retryOptions = {
      retries: 3,
      minTimeout: 1000,
      maxTimeout: 5000,
      onRetry: (error) => {
        console.error('Retrying due to error:', error);
      },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    步骤 3:重试操作

    将可能失败的操作包装在重试函数中,并传入重试选项。

    retry(async () => {
      // Operation that may fail
      await fetchSomeData();
    }, retryOptions)
      .then((result) => {
        console.log('Operation succeeded:', result);
      })
      .catch((error) => {
        console.error('Operation failed after retries:', error);
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在此示例中,async-retry库将fetchSomeData()方法指定的重试选项并进行自动重试操作。

    9. 日志记录和分析

    记录错误和收集分析对于故障排除和调试至关重要。正确的日志记录策略可以提供错误可见性并帮助跟踪应用程序性能。让我们看看所涉及的步骤:

    第 1 步:选择日志库或服务

    选择像 Winston 这样的日志库或像 ELK StackElasticsearchLogstashKibana)或 Splunk 这样的日志服务。

    第2步:配置日志系统

    使用适当的传输配置日志记录系统,例如控制台日志记录或文件日志记录,并设置日志级别。

    const winston = require('winston');
    
    const logger = winston.createLogger({
      level: 'info',
      format: winston.format.simple(),
      transports: [
        new winston.transports.Console(),
        new winston.transports.File({ filename: 'application.log' }),
      ],
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    步骤 3:记录错误和相关信息

    在应用程序代码中记录错误、堆栈跟踪和其他相关信息。

    try {
      // Code that may throw an error
    } catch (error) {
      logger.error('An error occurred:', error);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例1: 将错误记录到控制台

    const winston = require('winston');
    
    const logger = winston.createLogger({
      level: 'error',
      format: winston.format.simple(),
      transports: [new winston.transports.Console()],
    });
    try {
      // Code that may throw an error
      throw new Error('Something went wrong');
    } catch (error) {
      logger.error('An error occurred:', error);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在此示例中,我们使用 Winston 日志记录库将错误以错误日志级别记录到控制台。

    示例2: 将错误记录到文件

    const winston = require('winston');
    
    const logger = winston.createLogger({
      level: 'error',
      format: winston.format.simple(),
      transports: [new winston.transports.File({ filename: 'application.log' })],
    });
    try {
      // Code that may throw an error
      throw new Error('Something went wrong');
    } catch (error) {
      logger.error('An error occurred:', error);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在此示例中,我们配置 Winston 记录器将错误记录到名为application.log的文件中.

    结论

    Node.js 中的错误处理是构建健壮且可靠的应用程序的一个关键方面。通过遵循最佳实践(例如使用 try-catch 块、创建自定义错误类、处理异步错误以及实施错误日志记录和报告),我们可以有效地管理代码库中的错误并从中进行维护。

  • 相关阅读:
    (十五)Alian 的 Spring Cloud 自动生成项目
    [pytorch]手动构建一个神经网络并且训练
    天地图图层切换显示
    python实现分词器
    基于智能远程监考方案,云上组考打造考试新范式
    2.DesignForClines\4.BgaAutoRouting
    微服务实战 02 Sentinel 入门
    机器学习-集成算法
    【Python】第十一课 模块
    vue3+ts的computed属性
  • 原文地址:https://blog.csdn.net/qq_42880714/article/details/133257008