当前位置: 首页 > article >正文

Golang—— error 和 panic

本文详细介绍Golang的两种错误处理机制:error 和 panic。

在这里插入图片描述

文章目录

    • Golang 的错误处理机制概述
    • `error`
      • 特点
      • 代码示例
        • 基本用法
        • 创建 `error`
    • `panic`
      • 特点
      • 运行时错误示例
      • `defer` 和 `recover` 的结合使用
      • 代码示例
        • 基本用法
        • 创建 `panic`
      • `panic` 的执行机制
    • `error` 和 `panic` 的对比
    • 生产环境的建议
    • 生产环境中使用 `panic` 的场景
      • 启动阶段异常强制终止。
        • 场景
        • 示例
        • 不需要捕获
      • 异步 Goroutine 中的异常防护
        • 场景
        • 示例
        • 需要捕获

Golang 的错误处理机制概述

Golang 提供两种主要的错误处理机制:

  • error:处理程序员可预知、意料之中的错误,例如文件打开失败、输入不合法等。
  • panic:处理程序员无法预知的严重异常,例如数组越界、空指针引用等。这类错误通常是不可恢复的,可能导致程序崩溃。

error

特点

  • 用于表示可预见的、常规的错误。
  • 开发者需要主动处理 error。
  • 通过返回值传递错误。

代码示例

基本用法
func main() {
	content, err := ioutil.ReadFile("filepath")
	if err != nil {
		// 错误处理
		// 记录错误信息
		fmt.Println("Error:", err)
	} else {
		fmt.Println(string(content))
	}
}
  • 如果 err == nil,表示没有错误;否则需要对错误进行处理。
创建 error

Go 提供两种方式创建 error

  1. 使用 errors.New
    import "errors"
    fmt.Println(errors.New("错误"))
    
  2. 使用 fmt.Errorf
    import "fmt"
    fmt.Println(fmt.Errorf("错误"))
    

panic

特点

  • 表示不可预期的错误:程序员无法预测的运行时错误,例如空指针、数组越界。
  • 会导致程序崩溃:如果不加处理,panic 会中止程序运行。
  • 结合 deferrecover 使用:提供有限的恢复能力。

运行时错误示例

func main() {
	n := 0
	res := 1 / n // 引发 panic:除以零
	fmt.Println(res)
}

deferrecover 的结合使用

recover 是 Go 内置函数,用于捕获 panic,实现部分恢复程序控制权。

代码示例

基本用法
func handlePanic() {
	if err := recover(); err != nil {
		fmt.Println("Recovered from panic:", err)
	}
}

func main() {
	defer handlePanic() // 在 panic 前注册 defer
	n := 0
	res := 1 / n        // 引发 panic
	fmt.Println(res)    // 这行不会执行
}
  • defer 的执行顺序是先进后出,注册顺序很重要。
  • panic 被捕获时,recover 会返回 panic 的错误信息。

创建 panic

通过内置函数 panic,直接触发一个运行时错误,终止程序执行。

package main

func main() {
	panic("this is a panic")
}
  • 特点:触发后程序立即进入 panic 状态,中止执行当前函数,并调用已注册的 defer 函数。

panic 的执行机制

  • panic 发生:

    1. 程序中止运行。
    2. 当前 goroutine 中的所有 defer 会按逆序执行。
    3. 如果没有 recover,程序将崩溃并打印 panic 信息。
  • recover 的限制

    • 必须在 defer 中调用。
    • 只能捕获当前 goroutine 的 panic
    • defer要在panic之前先注册(defer必须在panic前面),否则不能捕获异常。defer 会确保即使发生 panic,仍然可以执行资源清理逻辑。

errorpanic 的对比

特性errorpanic
定义可预期的错误不可预期的严重异常
处理方式通过返回值传递,由调用方检查和处理通过 deferrecover 捕获
程序状态不中断程序中断程序
使用场景文件操作失败、网络超时等正常错误数组越界、空指针等致命错误
恢复能力错误处理后可恢复如果没有 recover 则无法恢复

总结:

  • error 是 Go 中主要的错误处理方式,适合处理常规错误。
  • panic 应用于不可恢复的错误,但应谨慎使用,避免影响程序健壮性。

生产环境的建议

实际开发中,使用Error会多,一些逻辑的判断,错误都会使用error,但是很少用到Panic。

以下是关于 生产环境中使用 panic 的场景是否需要捕获 panic


生产环境中使用 panic 的场景

在生产环境中,panic 的使用场景非常有限,通常只用于程序中无法恢复的严重错误。以下是一些实际使用 panic 的典型场景:

一般来说,常用的Web框架/任务调度系统都会在框架的出入口封装panic及其捕获逻辑(防御性编程和不可恢复的逻辑错误),程序的顶层入口函数中捕获所有未处理的 panic,用于记录日志、释放资源或优雅退出程序。

防御性编程:库函数或 API 接收到非法输入导致程序状态不安全时,可以使用 panic 作为防御手段,明确提示调用方。
不可恢复的逻辑错误:数据结构或状态出现严重问题,如数组越界、递归深度超限等,这种错误通常不可恢复。

  • 在 Web 服务中,一个请求引发的 panic 不应影响其他请求。
  • 任务调度系统中,一个任务的 panic 不应导致整个系统宕机。

上面是web框架在做的一些panic场景的封装,下面是两个在日常开发中,需要额外注意的情况:

启动阶段异常强制终止。

启动阶段是指 运行程序时 的初始化阶段,也就是程序开始执行 main.go 文件中的代码时。

场景
  • 启动阶段依赖的关键资源(如配置文件、数据库连接)缺失或初始化失败。
  • 如果这些问题无法解决,程序不应继续运行。
示例
package main

import (
	"fmt"
	"os"
)

func loadConfig(filePath string) {
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		panic(fmt.Sprintf("Critical error: Config file %s is missing", filePath))
	}
	fmt.Println("Configuration loaded successfully.")
}

func main() {
	loadConfig("missing_config.yaml") // 模拟配置文件缺失
}
不需要捕获

不需要。
此类问题属于致命错误,无法恢复,直接让程序崩溃是合理的选择,开发者需要修复问题。


异步 Goroutine 中的异常防护

场景
  • 同步 Goroutine 的生命周期和主 Goroutine 紧密相连。大多数 Web 框架、任务调度系统在框架出入口对 panic 进行统一封装,并在必要时捕获和处理。
  • 异步 Goroutine 的生命周期独立于主 Goroutine,通常运行在并发的上下文中,主 Goroutine 无法直接感知异步 Goroutine 的异常。所以更需要使用异常防护,如果异步 Goroutine 中发生 panic 且未捕获,会导致整个程序直接崩溃,并且难以排查错误原因。
示例
package main

import (
	"fmt"
	"time"
)

func safeGo(task func()) {
	go func() {
		defer func() {
			if r := recover(); r != nil {
				fmt.Println("Recovered from panic in goroutine:", r)
				// 此处可以添加错误日志记录逻辑或其他恢复措施
			}
		}()
		task()
	}()
}

func main() {
	safeGo(func() {
		panic("Task failed!") // 模拟任务失败
	})
	fmt.Println("Program continues running...")
	time.Sleep(2 * time.Second) // 等待 Goroutine 执行完成
}
需要捕获

原因

  1. 防止程序崩溃
    • Goroutine 中的异常如果未被捕获,会导致程序崩溃,特别是在关键服务中,这是不可接受的。
  2. 错误追踪
    • 捕获异常后可以记录日志,帮助开发人员分析错误发生的原因。
  3. 业务连续性
    • 即使某个任务失败,通过异常捕获可以让程序继续运行其他任务,保证服务稳定性。

注意事项

  • 明确恢复策略:捕获异常后,需要有明确的恢复策略,比如是否需要重试任务,或如何通知其他服务。
  • 避免滥用 recover:捕获异常是为了保护程序稳定运行,但也要避免滥用 recover,导致隐藏问题无法被及时发现和修复。
  • 日志记录:捕获异常后需要将详细的错误信息记录到日志中,方便后续排查。日志等级:Debug < Info < Warn < Error < Panic

http://www.kler.cn/a/506962.html

相关文章:

  • 大文件上传的解决办法~文件切片、秒传、限制文件并发请求。。。
  • 网管平台(进阶篇):路由器的管理实践
  • Spring Boot 下的Swagger 3.0 与 Swagger 2.0 的详细对比
  • 硬件知识:显示器发展历程介绍
  • .NET 9.0 的 Blazor Web App 项目中 Hash 变换(MD5、Pbkdf2) 使用备忘
  • springMVC实现文件上传
  • [私用学习笔记·突击面试]Collection和迭代器
  • 图论的起点——七桥问题
  • 软件测试入门—测试流程之需求分析
  • Pycharm报错:DeprecationWarning: sipPyTypeDict() is deprecated
  • react 与 vue 的比较,以及如何选择?
  • 使用 spring boot 2.5.6 版本时缺少 jvm 配置项
  • 持续集成 02|Jenkins介绍与安装、Postman集成Jenkins、代码集成Jenkins
  • 【网络协议】DHCP(动态主机配置协议)
  • 单片机的原理及其应用:从入门到进阶的全方位指南
  • iis强制重定向https
  • 出差人员携带的电脑文件信息安全如何保障?
  • 校园跑腿小程序---任务界面 发布以及后端模板下载
  • react中的hook
  • 浅谈云计算10 | 服务器虚拟化支撑技术(长文)
  • Spring Boot启动流程详解与技术探讨
  • UDP/TCP ①-UDP协议报文格式
  • 【网络篇】IP知识
  • JVM之垃圾回收器ZGC概述以及垃圾回收器总结的详细解析
  • Node.js新草案:通过共享库使用Node.js
  • 计算机网络开发基础实训室设备