Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结

Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结。

01 前言

在使用 Go 语言进行 Web 开发时,我们往往会选择一些优秀的库来简化 HTTP 请求的处理。其中,go-resty 是一个被广泛使用的 HTTP 客户端。正是因为使用了 go-resty 库,才有今天想要分享的主题:go-resty 在处理 HTTP 307/308 重定向状态码时的问题及其解决方案。

02 HTTP 状态码

相信大家都听过 HTTP 302 状态码,但是否都了解过 HTTP 307/308 状态码。让我们分别看看 HTTP 302、307 和 308 状态码的定义及其之间的区别:

  1. HTTP 302:
    名称:Found 或 Temporary Redirect。
    描述:这是一个临时重定向,意味着请求的资源已经临时移动到了一个新的 URL。此状态码表示原始的请求方法应该保留使用于新的 URL。但在实际应用中,很多客户端,特别是浏览器,可能会将 POST 转换为 GET。
    应用:在实际应用中,很多时候 302 重定向被用于 URL 短链接、登录状态验证等场景。

  2. HTTP 307:
    名称:Temporary Redirect。
    描述:和 HTTP 302 类似,HTTP 307 也表示资源已经临时移动到另一个 URL。但与 302 的主要区别是,HTTP 307 明确规定客户端必须不改变请求方法进行重定向。例如,如果原始请求是 POST,那么重定向请求也必须是 POST。
    应用:由于 307 更加明确,如果需要确保请求方法在重定向时不被更改,应使用 307 而不是 302。

  3. HTTP 308:
    名称:Permanent Redirect。
    描述:与 HTTP 301 (Permanent Redirect) 类似,HTTP 308 表示请求的资源已经被永久移动到了新的 URL。但与 301 的区别在于,308 明确规定客户端必须保持原始请求的方法不变。例如,POST 请求在 308 重定向后仍应为 POST。
    应用:当资源永久移动且希望确保请求方法不被更改时,应使用 308。

主要区别:
永久性 vs. 临时性:302 和 307 是临时重定向,而 308 是永久重定向。
请求方法的保留:302 不保证原始请求方法在重定向后仍会被保留;307 和 308 保证在重定向后请求方法不变。

03 项目背景

由于后台服务多区域部署,需要涉及到2个以上的环境数据打通,当请求到达 A 区域时,发现数据在 B 区域,会使用 HTTP 307 进行临时重定向,跳转到 B 区域。

在这里插入图片描述
正常请求是上面这样子的,下面来看看 go-resty 引起的问题:
当多个请求同时访问区域 A 时,存在请求 A 需要 307 重定向,而请求 B 也同时访问了区域 A,此时,由于 go-resty 重用缓存区导致的重定向请求 A 拿到了 请求 B的数据。
在这里插入图片描述

04 issue

go-resty 最新版本是tag:v2.7.0,这个版本已经是2021年11月份发布的,而在2022年5月份,就有人在 github 上提出了这个 issue,并进行了修复,最终也合并到了 master 主分支了,然而至今并没有发布新版本,这就导致线上出了这个重定向问题。
修复错误重用缓冲区导致的重定向请求正文与源请求正文不匹配

https://github.com/go-resty/resty/pull/568
下面,我们来看看这个 mr 修复了哪里的代码
在这里插入图片描述
可以看到,原本直接通过 bytes.NewBuffer(r.bodyBuf.Bytes()) 直接返回了,修复的方式是通过 io.Copy 来复制一个新的缓存区,解决重用的问题。

最终提交的代码也编写了单元测试来验证结果是否准确:


func TestPostRedirectWithBody(t *testing.T) {ts := createPostServer(t)defer ts.Close()targetURL, _ := url.Parse(ts.URL)t.Log("ts.URL:", ts.URL)t.Log("targetURL.Host:", targetURL.Host)c := dc()wg := sync.WaitGroup{}for i := 0; i < 100; i++ {wg.Add(1)go func() {defer wg.Done()resp, err := c.R().SetBody([]byte(strconv.Itoa(newRnd().Int()))).Post(targetURL.String() + "/redirect-with-body")assertError(t, err)assertNotNil(t, resp)}()}wg.Wait()
}

05 总结

当线上环境出现 307 重定向请求且并发量急剧变大时,同一时间出现碰撞的概率就变大了,就出现了串数据的情况,这是非常严重的问题。


总结Golang编程时常见的错误和问题:

unrecognized import path "golang.org/x/…
golang 在 github 上建立了一个镜像库,下载github上的镜像库放入GOPATH下即可

mkdir -p $GOPATH/src/golang.org/x
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/sync.git
git clone https://github.com/golang/crypto.git
git clone https://github.com/golang/sys.git

go包管理代理网址无法访问:proxy.golang.org
解决:

go env -w GOPROXY=https://goproxy.cn

verifying module: invalid GOSUMDB: malformed verifier id
解决:

关闭包验证

go env -w GOSUMDB=off

设置go env -w GOPROXY=***warning: go env -w GOPROXY=… does not override conflicting OS environment variable

解决方案:

unset GOPROXY

再重新设置即可

类型不匹配
在 Golang 中,类型的匹配是至关重要的。关于 Golang 类型匹配的一个经典错误是在比较两个不同类型的变量。例如,将字符串类型的变量与整数类型的变量进行比较。要避免这种错误,应该始终确保比较的是相同类型的变量。或者,在进行类型转换时,需确保变量类型符合要求。

空指针错误
在 Golang 中,指向空值的指针是非常常见的错误。这通常在代码中的资源管理方面出现问题,如打开和关闭文件,连接到数据库等。当处理指针变量时,应该始终确保其指向一个有效的对象。此外,对于指针变量的使用应该小心谨慎,以避免出现在编程过程中不同的指针指向同一个对象的情况。

内存分配错误
内存分配是计算机程序所必需的一项任务,但是在 Golang 中,这个任务可能会出现问题。最常见的错误是未能释放内存。在使用 Golang 中的 new() 或 make() 函数的时候,应该注意释放内存,以确保程序不会因为内存泄漏而崩溃。

大规模并发处理问题
在 Golang 中,大规模的并发处理是其一个显著的特点,但这也带来了一些问题。并发程序的错误处理比串行程序更为棘手,因为开发者需要在多个并发执行的地方确定错误。在开发 Golang 并发程序时,应该设计清晰的错误管理方案,并充分利用 Golang 提供的资源管理功能进行并发控制。

死锁
死锁是一个常见的 Golang 错误。大多数死锁发生在程序未能释放锁或加锁太多的情况下。更糟糕的是,用户可能需要重新启动程序来解决死锁的问题。为避免死锁,可以使用并发控制的技术确保子程序无法同时访问相同的资源。

文件处理问题
在 Golang 中,文件处理是经常进行的任务。常见的文件处理错误包括文件无法打开,读取和写入错误。为避免这些错误,应该选择一个完全合适的文件操作函数,并检查文件路径是否正确。同时,应该在解析文件时检查文件的格式,以确保文件中的数据已正确转换。

数组和切片的越界问题
在 Golang 中,数组和切片的越界访问是另一个可能的错误。由于数组和切片的索引是从 0 开始计数的,所以如果开发者在索引时错误地写入负数、超出边界的正数或者不同于整数的索引,就可能出现越界错误。为避免这个问题,开发者可以使用循环来遍历数组和切片,并始终确保数组和切片的索引值是有效的。

总结

以上是 Golang 中常见的错误。在 Golang 的学习过程中,这些错误可能会使新手们不知所措,但只要有足够的耐心和学习态度,并遵循最佳实践,就可以轻松地避免这些错误。


最近线上有个服务突然卡死了,看起来是进程启动了,但是请求接口则反应连接不上,看错误日志也没有什么异常信息,对于这种线上服务卡死的问题,一直没有丰富经验来排查。看网上一些人各自gdb,systemtap之类的工具调试线上问题,很是羡慕。

我先是用 strace -p pid 来看问题,发现有时候又处于wait futex,有时候有正常的刷刷刷一大堆信息。按照我之前的经验,出现wait futex,一般是等待channel出现,如果一直处于wait futex,则说明进程卡死在某个channel上了。但是现在这个卡死的进程并没有一直处于wait futex,说明可能不是卡死在某个channel上,至少还有其他goroutine在正常活动中。

进程没有卡死在wait futex,但是又不能监听端口处理请求,说明要么是在启动阶段,还未到监听端口这一步就出现死锁卡死了,要么就是监听端口的那部分代码出问题了。

以前我只用过strace -p 来判断是否死锁了,更早之前还试过用gdb调试python服务,但是go的服务,该用什么工具,还不太了解,理论上用strace和gdb也能排查到一些问题的,但是这两个工具都无法识别goroutine,调试起来很难。

后来同事介绍了 dlv,也就是delve这款工具,专门为go语言打造的调试工具,可以识别goroutine等。搜了下官方文档和网上的各自介绍文章,写得都不太实用,好多文章就是玩具级别的介绍,实际生产环境中使用很多细节都没说明。但是只能硬着头皮看一点说明介绍就试用一下,不过这种边解决问题边学习的方法,效率还是很高的。最终也是依靠dlv这款工具定位到了死锁之处以及引起问题所在。

安装:
参照官方文档,先执行

go install github.com/go-delve/delve/cmd/dlv@latest  

这样子会安装 dlv 到你的GOPATH目录下,里面会生成一个dlv的可执行文件。然后将这个dlv上传到服务器上就可以使用了。

当然也可以先拉取代码,然后在代码目录执行 go build,生成可执行文件。

attach和coredump:
有了dlv之后,第一步就是要让dlv可以接触上卡死的进程,也就是使用 attach指令把dlv附着在卡死的进程上。

首先,使用 ps -ef | grep xxxx 查看卡死的进程的pid,假设卡死的进程pid=1234,然后执行 ./dlv attach 1234 就 可以附着到卡死的进程上了。如果进程是使用非当前用户启动的话,则要加上 sudo 才行。

连接上进程之后,不管三七二十一,要赶紧导出coredump,因为一旦出现什么问题,导致进行死掉或者卡死状态解除了,就无法继续排查调试了。有了coredump,则可以一直慢慢来调试。

在dlv里,执行 dump ~/sample.core , 就会把当前整个进程的状态都记录下来,那样子就可以下载coredump文件,在自己的开发机上逐步调试问题所在,又同时不影响线上服务。

在自己的开发机上查看coredump文件,还需要提供和线上运行程序一致的二进制文件。比如你线上运行的是 v0.22 版本,要么你就直接从线上下载这个二进制文件,要么你可以把代码切换到v0.22,然后进行编译,同时编译参数不能有任何的变化。只有保证二进制文件与coredump对应的完全一致,才可以使用。

假设二进制文件名问:sample_bin,则在开发机上执行 dlv core sample_bin sample.core 就能加载到coredump文件进行排查问题了。

attach和coredump的区别,主要是attach是附着到一个运行中的实例中,是可以执行函数调用,设置断点等操作的,而coredump就只能静态的去查看栈帧、内存等信息了。因此,如果线上出现了类似卡死的故障,先是保存好coredump,如果可以的话,就直接在线上attach调试,这样子会更容易排查问题。

排查:

排查故障,主要用到的就几个指令:
bt,查看栈帧
list,查看当前栈帧运行到的代码
grs,查看当前所有的goroutine列表
gr 4,切换到第四个groutine
print xxx,打印当前栈帧中的变量值
args,打印函数参数值
locals,打印当前栈帧的local 变量

总结处理过程:

grs 查看goroutine列表
优先切换到 gr 1
执行bt指令看栈帧,查找到业务相关代码
frame xxx,切换到指定栈帧
list 查看当前栈帧运行到的代码
print xxx 查看变量值

遇到的问题:
线上调试的时候,想执行 list 查看当前栈帧的代码,出现了类似 Command failed: open /path/to/the/mainfile.go: no such file or directory 的错误。即使下载了coredump文件和二进制到自己的开发机上进行调试,也会出现这个问题,因为二进制是另一个同事编译的,Go的编译默认会带上代码的完整路径,而同事的GOPATH和我是不一样的,这个要怎么解决呢?

终极解决办法,编译参数加上 trimpath:go build -gcflags=-trimpath= G O P A T H − a s m f l a g s = − t r i m p a t h = GOPATH -asmflags=-trimpath= GOPATHasmflags=trimpath=GOPATH 这样子编译的代码路径,就会去掉你本人的文件夹路径,使用相对于GOPATH的路径了,别人在你编译的二进制上获取的coredump,也可以正常调试了。

如果编译前没有加上这个指令 ,则可以在 dlv 里执行 : config substitute-path otherPath mypath。

如果这个path弄错了,怎么清除弄错的配置呢?直接执行 config substitute-path otherPath 即可

注意 mypath一定要填绝大路径,使用 ~/ 这样子的路径会有问题。

config配置完之后,如果需要永久的,可以执行 config -save,这样子配置会默认保存在 ~/.config/dlv 目录里面。

不过另一个问题也难以解决,服务器上一般是不会带上Go代码及代码块的,所以在服务器上进行调试的时候,list指令就用不上了,很难排查问题。最终还是只能用coredump在开发机上排查调试?

其实也可以开启调试服务,线上开启,在开发机上用dlv连接线上开启的服务,就可以进行调试了。不过这个也挺麻烦的。
还遇到一个问题,是print变量的时候,如果是字符串变量,好像默认只打印前64个字符,这明显不够,可以通过设置:config max-string-len 99999 让打印的字符串长度加长。此时可以执行 config -save 把这个配置永久保存下来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/56103.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何有效进行RLHF的数据标注?

编者按&#xff1a;随着大语言模型在自然语言处理领域的广泛应用&#xff0c;如何从人类反馈进行强化学习&#xff08;RLHF&#xff09;已成为一个重要的技术挑战。并且RLHF需要大量高质量的人工数据标注&#xff0c;这是一个非常费力的过程。 本文作者在数据标注领域具有丰富经…

解决jupyter notebook可以使用pytorch而Pycharm不能使用pytorch的问题

之前我是用的这个目录下的Python 开始更新目录 1、 2、 3、

【javaweb】学习日记Day6 - Mysql 数据库 DDL DML

之前学习过的SQL语句笔记总结戳这里→【数据库原理与应用 - 第六章】T-SQL 在SQL Server的使用_Roye_ack的博客-CSDN博客 目录 一、概述 1、如何安装及配置路径Mysql&#xff1f; 2、SQL分类 二、DDL 数据定义 1、数据库操作 2、IDEA内置数据库使用 &#xff08;1&…

Python3 列表

Python3 列表 序列是 Python 中最基本的数据结构。 序列中的每个值都有对应的位置值&#xff0c;称之为索引&#xff0c;第一个索引是 0&#xff0c;第二个索引是 1&#xff0c;依此类推。 Python 有 6 个序列的内置类型&#xff0c;但最常见的是列表和元组。 列表都可以进…

飞腾uboot命令简单介绍

飞腾uboot和开源uboot并无大差异,故飞腾uboot固件命令可以直接从网上搜索开源uboot相关命令。 这里为了便于大家调试,将一些可能用到的命令说明一下。 在 Uboot 命令行下,输入 help 将打印所有的可用命令,复杂命令操作,通过命令 help 的方式获取具体说明。 1.help命令 …

时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价)

时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价) 目录 时序预测 | MATLAB实现DBN-SVM深度置信网络结合支持向量机时间序列预测(多指标评价)效果一览基本描述程序设计参考资料 效果一览 基本描述 MATLAB实现DBN-SVM深度置信网络结合支持向量机…

校招算法题实在不会做,有没有关系?

文章目录 前言一、校招二、时间复杂度1、单层循环2、双层循环 三、空间复杂度四、数据结构五、校招算法题实在不会做&#xff0c;有没有关系&#xff1f;六、英雄算法集训 前言 英雄算法联盟八月集训 已经接近尾声&#xff0c;九月算法集训将于 09月01日 正式开始&#xff0c;目…

.NET 8 Preview 7 中的 ASP.NET Core 更新

作者&#xff1a;Daniel Roth 排版&#xff1a;Alan Wang .NET 8 Preview 7 现在已经发布&#xff0c;其中包括了对 ASP.NET Core 的许多重要更新。 以下是预览版本中新增功能的摘要&#xff1a; 服务器和中间件 防伪中间件 API 编写 最小 API 的防伪集成 Native AOT 请求委托…

市值暴跌后,每日优鲜能否靠2亿融资“续命”?

濒临破产退市的每日优鲜&#xff0c;靠转型实现“自救”&#xff1f; 作为“生鲜电商第一股”&#xff0c;每日优鲜在上市1年后爆发生存危机。 8月4日&#xff0c;每日优鲜(NDAQ:MF)公布了2022年报&#xff0c;尽管去年7月其宣布关闭营收占比约90%的DWM业务&#xff0c;全面终…

【80天学习完《深入理解计算机系统》】第十一天 3.5 过程(函数调用)

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

RK3568 安卓源码编译

一.repo安卓编译工具 项目模块化/组件化之后各模块也作为独立的 Git 仓库从主项目里剥离了出去&#xff0c;各模块各自管理自己的版本。Android源码引用了很多开源项目&#xff0c;每一个子项目都是一个Git仓库&#xff0c;每个Git仓库都有很多分支版本&#xff0c;为了方便统…

gradio使用transformer模块demo介绍2:Images Computer Vision

文章目录 图像分类 Image Classification图像分割 Image Segmentation图像风格变换 Image Transformation with AnimeGAN3D模型 3D models 图像分类 Image Classification import gradio as gr import torch import requests from torchvision import transformsmodel torch.…

【Unity3D赛车游戏】【六】如何在Unity中为汽车添加发动机和手动挡变速?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

【【STM32分析IO该设置什么模式的问题】】

STM32分析IO该设置什么模式的问题 我们分析而言 我们对于PA0 的设计就从此而来 对于边沿触发的选择我们已经有所了解了 我们下拉&#xff0c;但是当我们摁下开关的时候 从0到1 导通了 所以这个是下拉 上升沿触发 而对于KEY0 我们摁下是使得电路从原来悬空高阻态到地就是0 所以…

龙芯2K1000LA移植交叉编译环境以及QT

嵌入式大赛结束了&#xff0c;根据这次比赛中记的凌乱的笔记&#xff0c;整理了一份龙芯2K1000LA的环境搭建过程&#xff0c;可能笔记缺少了一部分步骤或者错误&#xff0c;但是大致步骤可以当作参考。 一、交叉编译工具链 下载连接&#xff1a;龙芯 GNU 编译工具链 | 龙芯开…

几个nlp的小项目(文本分类)

几个nlp的小项目(文本分类) 导入加载数据类、评测类查看数据集精确展示数据测评方法设置参数tokenizer,token化的解释对数据集进行预处理加载预训练模型进行训练设置训练模型的参数一个根据任务名获取,测评方法的函数创建预训练模型开始训练本项目的工作完成了什么任务?导…

Flask 单元测试

如果一个软件项目没有经过测试&#xff0c;就像做的菜里没加盐一样。Flask 作为一个 Web 软件项目&#xff0c;如何做单元测试呢&#xff0c;今天我们来了解下&#xff0c;基于 unittest 的 Flask 项目的单元测试。 什么是单元测试 单元测试是软件测试的一种类型。顾名思义&a…

redis--集群

redis集群 Redis 集群是一种用于分布式存储和管理数据的解决方案&#xff0c;它允许将多个 Redis 实例组合成一个单一的逻辑数据库&#xff0c;提供更高的性能、容量和可用性。 redis集群的优点 高可用性&#xff1a; Redis集群使用主从复制和分片技术&#xff0c;使得数据可…

centos7安装hadoop 单机版

1.解压 &#xff08;1&#xff09;将hadoop压缩包复制到/opt/software路径下 &#xff08;2&#xff09;解压hadoop到/opt/module目录下 [rootkb135 software]# tar -zxvf hadoop-3.1.3.tar.gz -C /opt/module/ &#xff08;3&#xff09;修改hadoop属主和属组 [rootkb135 m…

MySQL索引 事物 存储引擎

一 索引 索引的概念 索引就是一种帮助系统能够更快速的查找信息的结构 索引的作用 索引的副作用 创建索引的规则 MySQL的优化 哪些字段/场景适合创建索引 哪些不适合 小字段唯一性强的字段更新不频繁&#xff0c;但查询率比较高的字段表记录超过 300行主键&#xff0c;外键…