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

相关文章

关于DNS域名解析服务

《关于DNS域名解析服务》:本文主要介绍关于DNS域名解析服务,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录DNS系统的作用及类型DNS使用的协议及端口号DNS系统的分布式数据结构DNS的分布式互联网解析库域名体系结构两种查询方式DNS服务器类型统计构建DNS域

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

go中的时间处理过程

《go中的时间处理过程》:本文主要介绍go中的时间处理过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 获取当前时间2 获取当前时间戳3 获取当前时间的字符串格式4 相互转化4.1 时间戳转时间字符串 (int64 > string)4.2 时间字符串转时间

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

java向微信服务号发送消息的完整步骤实例

《java向微信服务号发送消息的完整步骤实例》:本文主要介绍java向微信服务号发送消息的相关资料,包括申请测试号获取appID/appsecret、关注公众号获取openID、配置消息模板及代码... 目录步骤1. 申请测试系统2. 公众号账号信息3. 关注测试号二维码4. 消息模板接口5. Java测试