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

相关文章

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

sysmain服务可以禁用吗? 电脑sysmain服务关闭后的影响与操作指南

《sysmain服务可以禁用吗?电脑sysmain服务关闭后的影响与操作指南》在Windows系统中,SysMain服务(原名Superfetch)作为一个旨在提升系统性能的关键组件,一直备受用户关... 在使用 Windows 系统时,有时候真有点像在「开盲盒」。全新安装系统后的「默认设置」,往往并不尽编

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Java 正则表达式的使用实战案例

《Java正则表达式的使用实战案例》本文详细介绍了Java正则表达式的使用方法,涵盖语法细节、核心类方法、高级特性及实战案例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、正则表达式语法详解1. 基础字符匹配2. 字符类([]定义)3. 量词(控制匹配次数)4. 边

Python Counter 函数使用案例

《PythonCounter函数使用案例》Counter是collections模块中的一个类,专门用于对可迭代对象中的元素进行计数,接下来通过本文给大家介绍PythonCounter函数使用案例... 目录一、Counter函数概述二、基本使用案例(一)列表元素计数(二)字符串字符计数(三)元组计数三、C

Go之errors.New和fmt.Errorf 的区别小结

《Go之errors.New和fmt.Errorf的区别小结》本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考... 目录error的基本用法1. 获取错误信息2. 在条件判断中使用基本区别1.函数签名2.使用场景详细对

Nginx中配置使用非默认80端口进行服务的完整指南

《Nginx中配置使用非默认80端口进行服务的完整指南》在实际生产环境中,我们经常需要将Nginx配置在其他端口上运行,本文将详细介绍如何在Nginx中配置使用非默认端口进行服务,希望对大家有所帮助... 目录一、为什么需要使用非默认端口二、配置Nginx使用非默认端口的基本方法2.1 修改listen指令