Go 语言生产服务故障案例精析

2024-08-29 18:12

本文主要是介绍Go 语言生产服务故障案例精析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        大多数 Go 开发者都停留在简单的增删改查层面,对 Go 语言本身掌握程度不够,对常用依赖或者开源组件掌握不够,在开发项目过程中总会不经意间引入一些千奇百怪的问题,并且在遇到线上问题时往往束手无策。下面列举一些线上问题以及相应的解决思路,希望大家能从这些问题中吸取经验,总结出一套属于自己的解决问题的方法论。

1. 两种情况导致 502 状态码的情况

        服务端开发最常见的问题可能就是 HTTP 状态码异常了,其中 502 状态码最常见并且最复杂。

1.1.1 panic 异常

        我们可以将 Go 服务中的 panic 异常分为两种:一种是请求级别的 panic 异常,即 Go 服务在处理 HTTP 请求时发生了 panic 异常;与之相对的,我们称之为服务级别的 panic 异常。需要说明的是,两种类型的 panic 异常都会导致 502 状态码。

        1.1.1.1 panic 异常导致 502 状态码

        下面先来介绍服务级别的 panic 异常是如何导致 502 状态码的。服务级别的 panic 异常会导致 Go 服务异常退出,这时候网关侧必然会返回大量 502 状态码,同时网关侧会出现大量的错误日志,如下所示:

connect () failed (111: Connection refused) while connecting to upstream

        从上面的日志可知,网关发起 HTTP 请求需要先建立 TCP 连接,但是 Go 服务已经退出了,即没有进程在监听目标端口了,TCP 连接自然也就无法建立了,于是网关便向客户端返回了 502 状态码。这种情况还是比较容易处理的,只需要使用函数 recover 捕获异常就能避免 Go 服务的退出,参考下面的代码:

defer func(){if err := recover(); err != nil {buf = buf[:runtime.Stack(buf,false)]log.Fatalf("go panic err:%v \n stack:%s",err,buf)}
}()

        接下来讲解请求级别的 panic 异常是如何导致 502 状态码的。我们先写一个简单的程序验证一下,代码如下所示:

package mainfunc main() {server := &http.Server{Addr: "0.0.0.0:8080",}http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {panic("panic test")w.Write([]byte(r.URL.Path + ">ping response"))})_ = server.ListenAndServe()
}

        在上面的代码中,我们在 HTTP 请求处理函数中抛出了 panic 异常。另外需要再次强调,本例中的访问链路是客户端--网关Nginx--Go 服务。编译上面的程序,并通过 curl 命令发起 HTTP 请求,结果如下所示:

[root@localhost ~]#curl --request POST 'http://127.0.0.1/ping' -v
<HTTP/1.1 502 Bad Gateway

        由上面的结果可知,客户端确实收到了 502 状态码,并且多次执行 crul 命令的结果都是一样的。另外,如果你这时候看控制台,你会发现 Go 服务并没有退出,但是控制台输出了以下日志:

2024/08/28 09:59:20 http: panic serving xxxx:56850: panic test
goroutine 6 [running]:
net/http.(*conn).serve.func1()

        参考上面的输出结果,Go 服务没有退出,说明一定有函数 recover 捕获了异常,并输出了协程调用栈,可是既然都捕获 panic 异常了,为什么网关返回的还是 502 状态码呢?我们可以查看网关的错误日志,如下所示:

[error] upstream prematurely closed connection while reading response header from upstream

         参考上面的错误日志,网关 Nginx 在等待上游 Go 服务返回 HTTP 响应时,上游 Go 服务过早地关闭了 TCP 连接。为什么呢? 估计是 Go 服务在处理 HTTP 请求时, 使用函数 recover 捕获了异常,并关闭了 TCP 连接。是这样吗?我们简单看一下 Go 语言底层处理 HTTP 请求的逻辑,如下所示:

func (c *conn) serve(ctx context.Context){defer func(){if err := recover();err != nil && ErrAbortHandler {......c.server.logf("http:panic serving %v:%v\n%s",c.remoteAddr,err,buf)c.close()}}()
}

        在上面的代码中,针对 TCP 连接,Go 语言都会创建新的协程来处理从该连接接收到的 HTTP 请求,并且使用了函数 recover 来捕获 panic 异常。可以看到,当发生了 panic 异常之后,Go 语言一方面输出了协程调用栈来帮助开发者排查问题,另一方面直接关闭了 TCP 连接,这也是网关 Nginx 返回 502 状态码的根本原因。

        最后总结下,请求级别的 panic 异常同样会导致 502 状态码。幸运的是,这种情况的 502 非常容易排查:一来我们可以在上游 Go 服务标准输出查看到错误日志;

这篇关于Go 语言生产服务故障案例精析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1118583

相关文章

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Go路由注册方法详解

《Go路由注册方法详解》Go语言中,http.NewServeMux()和http.HandleFunc()是两种不同的路由注册方式,前者创建独立的ServeMux实例,适合模块化和分层路由,灵活性高... 目录Go路由注册方法1. 路由注册的方式2. 路由器的独立性3. 灵活性4. 启动服务器的方式5.

TP-Link PDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务

《TP-LinkPDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务》近期,路由器制造巨头普联(TP-Link)在用户群体中引发了一系列重要变动,上个月,公司发出了一则通知,明确要求所... 路由器厂商普联(TP-Link)上个月发布公告要求所有用户必须完成实名认证后才能继续使用普联提供的 D

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

微服务架构之使用RabbitMQ进行异步处理方式

《微服务架构之使用RabbitMQ进行异步处理方式》本文介绍了RabbitMQ的基本概念、异步调用处理逻辑、RabbitMQ的基本使用方法以及在SpringBoot项目中使用RabbitMQ解决高并发... 目录一.什么是RabbitMQ?二.异步调用处理逻辑:三.RabbitMQ的基本使用1.安装2.架构