defer+recover保护gin不panic

Go panic/recover机制深度解析与生产防护实践 panic是Go语言中程序不可恢复崩溃的运行时信号,其本质并非错误处理而是goroutine级的紧急终止机制;recover并非异常捕获,而是在defer链中对panic传播的精准拦截;defer则构成panic传播路径上唯一的可控拦截点。理解三者协同关系,是构建高可用Go服务的基础能力。该机制支撑了HTTP中间件全局兜底、数据库操作安全封装、并发goroutine隔离等关键工程实践,广泛应用于微服务、支付网关、消息队列等对稳定性要求严苛的生产场景。 阅读详情

本文首发于我的Github博客

本文记录了作者保护gin构建的web app不panic的方式,简单来说:

  1. 主程中的panic本身是会被gin拦截的
  2. 协程中的panic需要使用defer和recover进行保护

情景

在我们用gin构建,运行web app并上线了之后,或许有一些请求会经过业务,在特定的情况下出发会触发golang中的panic

按照golang的设定,一旦panic,如果不在函数调用栈中存在recover,那么是一定会使得整个程序终止的

但是线上的服务是不能够因为一个两个的请求就直接终止了的,这样非常危险,所以我们需要手段来阻止web app在panic的情况下直接终止

解决方案

主程序中的panic

对于gin这个web框架来说,主程序中的panic是会被自动recover的,还会打印出非常详细的日志信息,比如

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/panic", func(ctx *gin.Context) {
        panic("panic")
    })
    r.Run()
}

运行之后我们作如下HTTP请求

> curl localhost:8080/panic

会发现在gin的运行窗口出现如下信息

2020/06/04 18:42:12 [Recovery] 2020/06/04 - 18:42:12 panic recovered:
GET /panic HTTP/1.1
Host: localhost:8080
Accept: */*
User-Agent: curl/7.64.1


panic
/Users/admin/go/src/gin-test/main.go:10 (0x1581418)
        main.func1: panic("panic")
/Users/admin/go/src/github.com/gin-gonic/gin/context.go:165 (0x156baca)
        (*Context).Next: c.handlers[c.index](c)
/Users/admin/go/src/github.com/gin-gonic/gin/recovery.go:83 (0x157fb13)
        RecoveryWithWriter.func1: c.Next()
/Users/admin/go/src/github.com/gin-gonic/gin/context.go:165 (0x156baca)
        (*Context).Next: c.handlers[c.index](c)
/Users/admin/go/src/github.com/gin-gonic/gin/logger.go:241 (0x157ec40)
        LoggerWithConfig.func1: c.Next()
/Users/admin/go/src/github.com/gin-gonic/gin/context.go:165 (0x156baca)
        (*Context).Next: c.handlers[c.index](c)
/Users/admin/go/src/github.com/gin-gonic/gin/gin.go:420 (0x1575d20)
        (*Engine).handleHTTPRequest: c.Next()
/Users/admin/go/src/github.com/gin-gonic/gin/gin.go:376 (0x157548c)
        (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/local/Cellar/go/1.13.8/libexec/src/net/http/server.go:2802 (0x12cb6d3)
        serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
/usr/local/Cellar/go/1.13.8/libexec/src/net/http/server.go:1890 (0x12c6f74)
        (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
/usr/local/Cellar/go/1.13.8/libexec/src/runtime/asm_amd64.s:1357 (0x105c030)
        goexit: BYTE    $0x90   // NOP

[GIN] 2020/06/04 - 18:42:12 | 500 |    1.238546ms |             ::1 | GET      "/panic"

并且整个app还在正常运行,没有终止,这非常好

协程中的panic

不过非常可惜的是,对于协程中的panic,gin并不能做到自动recover并打印日志信息,比如

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/go-panic", func(ctx *gin.Context) {
        go func() {
            panic("panic")
        }()
    })
    r.Run()
}

运行该app之后,我们作如下的HTTP请求

> curl localhost:8080/go-panic

会发现gin app退出了

panic: panic

goroutine 24 [running]:
main.main.func1.1()
        /Users/admin/go/src/gin-test/main.go:11 +0x39
created by main.main.func1
        /Users/admin/go/src/gin-test/main.go:10 +0x35
exit status 2
协程解决方案

所以,对于协程,我们要手动进行defer和recover,来避免app的退出和打印日志信息,比如上面的代码应该修改为

package main

import (
    "fmt"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/go-panic", func(ctx *gin.Context) {
        go func() {
            defer func() {
                if err := recover(); err != nil {
                    fmt.Printf("error: %v\n", err)
                }
            }()
            panic("panic")
        }()
    })
    r.Run()
}

而后我们像刚才一样进行HTTP请求

> curl localhost:8080/go-panic

会得到如下打印

error: panic
[GIN] 2020/06/04 - 18:50:20 | 200 |       2.951µs |             ::1 | GET      "/go-panic"

可以看到app正常响应了请求,并且没有退出并打印了日志,想要更多定制操作可以修改defer的函数

Go语言核心特性:指针、init函数、闭包与defer详解 指针作为编程语言中的基础概念,通过直接操作内存地址实现高效数据访问。Go语言在保留指针强大功能的同时,通过类型安全、自动内存管理等机制降低了使用风险。理解指针原理有助于优化大对象传递、实现接口方法等场景。init函数是Go特有的初始化机制,通过严格的执行顺序保证程序正确启动。闭包将函数与引用环境结合,在中间件、资源管理等场景表现优异。defer语句以FILO顺序执行延迟调用,是资源清理的理想选择。本文深入解析这些Go核心特性,结合内存逃逸分析、并发安全等工程实践,帮助开发者规避常见陷阱,提升代码质量与性能。 阅读详情

相关推荐

Go函数定义与调用:从签名契约到栈帧执行的底层解析

函数是编程语言最基础的抽象单元,其本质是具备明确输入输出、可复用、可组合的代码块。在Go中,函数设计严格遵循类型安全、显式声明和编译期绑定原则,核心体现为不可变签名、无重载机制及接收者语义。这种设计带来零运行时开销、高并发安全性和强IDE支持等技术价值,广泛应用于HTTP Handler、微服务gRPC接口、并发任务调度等工程场景。理解函数在内存中的栈帧布局、参数传递方式(值拷贝 vs 指针)、返回值生命周期及defer/panic/goroutine协同机制,是写出高性能、低bug Go代码的关键前提——

chunqingtai2922的博客 349

7. Gin中间件-Panic处理

Gin中间件Panic处理

菜鸟的博客 1530

Go函数设计:从语法糖到生产级契约的深度解析

Go函数远不止是基础语法结构,而是运行时数据流、错误传播与资源管理的核心契约机制。其命名返回值、参数传递语义(如string/slice零拷贝)、defer绑定时机等特性,深刻影响内存分配、GC压力与并发安全性。在高并发微服务场景中,不当的函数签名(如大结构体传值、error处理缺失、指针逃逸)直接导致性能瓶颈与panic频发。本文结合opencode go实践与go并发编程真实案例,系统拆解函数定义、参数传递、返回值设计及main生命周期管理等关键链路,为已具备Go开发经验的工程师提供可落地的稳定性优化路

weixin_34090562的博客 399

「Go框架」gin框架是如何处理panic的?

在gin中,是通过使用该中间件来捕获panic,并保证服务不down机的。在defer函数中,如果捕获了panic,则将panic的详细详细记录下来,可以发送到指定的输出中,即函数中指定的out参数(默认是os.Stderr),也可以指定其他的文件或Sentry等。当然,这在生产环境下是不可接受的。首先,在golang中,如果在子协程中遇到了panic,那么主协程也会被终止。在gin中,正是该中间件的应用,确保了web服务的健壮性。当然,其他的web框架也有同样的机制,实现原理也是一样的。

ldxxxxll的博客 847

Go指针原理与nil安全实践:从内存模型到GC优化

指针是编程语言中管理内存和共享数据的基础机制,其核心在于地址获取、类型绑定与安全解引用。Go指针摒弃了C风格的指针算术和强制转换,采用显式可控、隐式安全的设计哲学,使编译器能精确执行逃逸分析,并为垃圾回收器提供可预测的标记路径。这种设计直接支撑了Go GC毫秒级STW(Stop-The-World)的低延迟特性,也决定了nil在Go中是合法零值而非错误标志。在Web服务(如http.ListenAndServeTLS)、并发编程(sync.Pool)、序列化(json.Unmarshal)等典型场景中,正确

weixin_30924239的博客 329

Go错误增强:为error添加结构化上下文与可追溯性

在Go语言中,原生error接口缺乏上下文信息,导致生产环境故障定位困难。其核心原理在于error包装机制(如%w动词)仅支持单向Unwrap,易造成语义丢失与链路断层;而errors.Is/As依赖Unwrap递归遍历,存在性能与深度风险。技术价值体现在提升错误可追溯性、支持结构化日志与精准监控告警。典型应用场景包括HTTP微服务错误透传、数据库操作参数快照、Gin框架集成及panic捕获增强。本文聚焦Go工程化落地中的真实痛点,提供轻量自研ContextError方案,兼顾类型安全、字段可提取与零反射开

hqmln的博客 1964

gin恢复中间件的作用&源码解释

Gin 的恢复中间件(Recovery Middleware)的作用是确保在处理 HTTP 请求时,应用程序不会因为某个处理函数中的 panic(运行时恐慌)而崩溃。相反,中间件会捕获 panic,记录错误信息,并返回一个合适的 HTTP 响应。

weixin_63860405的博客 657

Go中间件模式:洋葱模型与责任链

中间件模式用函数包装handler,在业务逻辑前后插入公共逻辑。洋葱模型的前处理按注册顺序执行,后处理逆序执行。recover放最外层捕获所有异常,auth放在限流前面防止未授权请求消耗限流配额。gin用c.Next()和c.Abort()控制执行流,context在中间件间传递数据。下一篇我们聊工厂模式,看看怎么用map注册创建不同类型的对象。

白话机器学习 89

gin.recovery,gin.default,gin.new浅解

gin.Recovery()/gin.Default()/gin.New()

qq_34252060的博客 2216

【影像组学入门百问】1#---#3

理论上讲三大主流平台Windows, Linux和MacOS均可进行影像组学研究。根据笔者实际经验,如果内存不足可能导致软件罢工,其余显示、GPU等均非必要。显示:最小分辨率为1024×768(推荐为1280×1024或更好)。Slicer中的许多计算都是多线程的,并且将受益于多核,多CPU配置。计算机需要足够的内存和图形功能将原始数据和处理结果保存在其内存中。如涉及深度学习等方法的研究需根据实际需求配置电脑,无法一概而论。接口设备:推荐使用带滚轮的三键鼠标。内存:超过4GB(推荐8G以上)。

weixin_38594676的博客 1484

告别裸机:在FreeRTOS上为STM32移植SOEM EtherCAT主站的思路与实战

本文详细介绍了在FreeRTOS环境下为STM32移植SOEM EtherCAT主站的工程实践。通过重构任务调度模型、优化内存管理和中断响应,成功将通信周期抖动控制在±100ns以内,满足工业控制系统的严苛实时性要求。文章包含完整的移植策略、性能优化方法和实测数据,为开发者提供了一套可靠的EtherCAT主站解决方案。

weixin_30342363的博客 388

Go panic不是异常处理,而是程序崩溃时的急救开关

panic是Go语言中用于应对不可恢复致命错误的运行时机制,其本质不同于传统异常处理,而更接近操作系统级的‘内核恐慌’;它基于栈展开与goroutine终止原理,代价高昂且不可跨协程捕获;技术价值在于保障系统状态可信性与快速失败(Fail Fast),适用于初始化校验、不变量断言等场景;在HTTP中间件、Worker Pool任务调度、服务启动配置验证等工程实践中,需严格区分panic与error的职责边界,避免滥用recover掩盖真实问题;本文深入解析panic的真实定位、recover的认知陷阱及分层

WGH100817的博客 404

UOS忘记密码别慌!用LiveCD工具5分钟搞定,附命令行救援模式详细步骤

本文详细介绍了UOS系统密码遗忘时的两种解决方案:图形化LiveCD工具和命令行救援模式。针对普通用户,LiveCD工具提供5分钟快速重置密码的便捷操作;而技术人员可通过命令行深度解析系统分区挂载与密码修改流程。文章还包含安全加固建议,帮助用户预防类似问题发生。

weixin_27032989的博客 589

recover 没有捕获异常_[系列] goginapi 路由中间件 捕获异常(四)

概述首先同步下项目概况:上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。当系统发生异常时,提示 “系统异常,请联系管理员!”,并发送panic告警邮件。什么是异常?在 Go 中异常就是 panic,它是在程序运行的时候抛出的,当 panic 抛出之后,如果在程序里没有添加任何保护措施的话,控制台就会在打印出 panic 的详细情况,然后终止运行。...

weixin_29281915的博客 218

别再手动调参数了!用3ds Max的tyDiffusion插件,5分钟搞定AI动态纹理生成

本文介绍了3ds Max的tyDiffusion插件如何通过AI技术快速生成动态纹理,大幅提升3D美术师的工作效率。从火焰、水流到布料飘动,tyDiffusion能在5分钟内完成传统手工需要数小时的任务,且效果更自然。文章详细解析了插件的核心功能、实战配置及进阶应用,帮助用户轻松实现AI动态纹理生成。

weixin_30919429的博客 667

FreeRTOS系统CPU使用率统计

操作系统中CPU使用率是在软件架构设计中必须要考虑的一个重要性能指标。它直接影响到程序的执行时间以及优先级更高的任务能否实时响应的问题。而CPU使用率也不能过低,避免资源浪费。

灵哎惹,凌沃敏的博客 1378

c++ 未捕获的异常_Gin 框架系列 — 路由中间件 捕获异常

概述首先同步下项目概况:上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。当系统发生异常时,提示 “系统异常,请联系管理员!”,并发送panic告警邮件。什么是异常?在 Go 中异常就是 panic,它是在程序运行的时候抛出的,当 panic 抛出之后,如果在程序里没有添加任何保护措施的话,控制台就会在打印出 panic 的详细情况,然后终止运行。...

weixin_33747775的博客 383

从零搭建无人机仿真环境:ROS 2与Gazebo Fortress实战指南

本文提供了一份详细的无人机仿真环境搭建实战指南,基于Ubuntu 22.04系统,整合ROS 2 Humble与Gazebo Fortress两大核心工具。通过分步讲解安装、配置及桥接过程,并引入PX4飞控与XTDrone2工具链,帮助开发者快速构建一个稳定、高效的无人机算法开发与测试平台,有效降低真机测试的成本与风险。

water的专栏 428

c++ 未捕获的异常_[系列] - go-gin-api 路由中间件 - 捕获异常(四)

概述首先同步下项目概况:上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。当系统发生异常时,提示 “系统异常,请联系管理员!”,同时并发送 panic 告警邮件。什么是异常?在 Go 中异常就是 panic,它是在程序运行的时候抛出的,当 panic 抛出之后,如果在程序里没有添加任何保护措施的话,控制台就会在打印出 panic 的详细情况,然后终止运行。我们可...

weixin_35890184的博客 92

信贷系统学习总结(6)—— 贷前、贷中、贷后全流程核心模型解析

本文系统解析了金融风控模型的技术演进与应用实践。从贷前、贷中到贷后全流程,详细介绍了A卡、B卡、C卡三类核心评分模型,以及反欺诈、动态监控、催收优化等关键技术的实现路径。重点阐述了特征工程设计、模型验证指标、联邦学习等创新应用,并提供了多个实战案例数据。同时提出了算法选择的"黄金三角"原则和分阶段技术演进路线,强调数据治理、模型全生命周期管理和复合型团队建设的重要性。文章指出未来风控将向决策智能化、数据无感化和风险预知化方向发展,需要平衡技术创新与合规要求。

科技D人生 1220

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

[系列] - go-gin-api 路由中间件 - 捕获异常(四)

概述首先同步下项目概况:上篇文章分享了,路由中间件 - 日志记录,这篇文章咱们分享:路由中间件 - 捕获异常。当系统发生异常时,提示 “系统异常,请联系管理员!”,并发送...

新亮笔记 2888

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

golang面试题

通道(channel)是 Go 语言用于 goroutine 间通信和同步的工具。就像一个传送带,一端的 goroutine 可以把数据放上去(发送数据),另一端的 goroutine 能从上面取走数据(接收数据),保障数据安全有序传递,避免数据竞争问题。数组:定义时必须明确指定长度,且该长度固定,不可更改。数组长度属于其类型的一部分,不同长度的数组属于不同类型。切片:是对数组的抽象,长度可在运行时动态改变。它本身不存储数据,而是指向一个底层数组。

qq_66313178的博客 1795

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Go错误处理实战:构建可诊断、可分类、可降级的错误流

在Go语言中,错误不是异常而是值,其核心设计哲学是将失败作为显式控制流纳入函数契约。理解error接口的本质、掌握errors.As/errors.Is的语义匹配能力、区分Transient/Business/Fatal错误级别,是实现高可靠服务的基础。本文围绕API调用中常见的'404 not found'、'stream disconnected'等典型错误场景,详解如何通过自定义错误类型(如APIError、NetworkError)、HTTP客户端自动包装、中间件分级重试及结构化日志监控,构建一套可

weixin_34008784的博客 598

Golang基础面试题汇总

channel、goroutine、slice、map、反射、内存、GC、Musql、Redis、ELK、K8s、Http、Https

weixin_41760738的博客 5696
上一篇: bash中的字符串处理操作
下一篇: 常见的git工作流
FitzLeopard
博客等级 码龄9年 6粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值