其实这个问题是《golang操作华为防火墙ACL实现远程启停》里遇到的,项目做完准备交付,测试偶尔遇到前端一直转圈(前后端不分离项目)。经过定位发现卡在了获取防火墙策略状态那里,再fiddler抓包看到了请求发出后一直没数据返回,加上默认http.Client没设置超时处理,导致后端也无法返回数据。
为什么restConf服务不返回数据呢?用fiddler直接模拟请求,也卡住了,根本没返回消息;再用python来请求,也是没任何数据响应;最后重启restConf服务,api https enable
,问题暂时解决,但是前端多刷新几次后,又出现卡顿了。
上面的问题不知啥情况,后面又复现不了了,但又出现了新的问题。golang项目里,只要多发起几次请求,请会出现401 Unauthorized报错。代码大概如下:
func Execute(method string,url string){ tlsCfg := &tls.Config{ InsecureSkipVerify: true, MaxVersion: tls.VersionTLS12, } tr := &http.Transport{ TLSClientConfig: tlsCfg, } client := &http.Client{ Transport: tr, } request, _ := http.NewRequest(method, url, nil) client.Do(request) ... }
经过多次反复对比测试和抓包,发现了个奇怪的现象:golang项目里刷新几次不行后(401 Unauthorized),不管再用哪种语言写的客户端,都报401错误。更让人费解的是,除了重启restConf服务外,通过防火墙后台把密码用同一密码重置后,再请求,竟然又恢复成功了。此外,当出现401时,我在后台再新添加个restConf的认证帐号,也能成功请求,但换回前一个帐号,又不行了。
疑问:难道是防火墙做了api调用限制?
想到这我立即用curl不断请求,发现n次都正常,用python请求也正常,但换回golang才请求几次就出现401了,这下确认了是golang的问题,跟防火墙没关。于是一行行代码分析着,尽量将代码精简,只保留关键代码。起一个for循环测试,才几次就报401了。难道是golang太快了?于是加上time.Sleep延时到5秒请求一次,再测试,一样的现象。从抓包的情况来看,正常请求和异常请求发出的数据没任何异常情况,搞不懂是啥回事。
再回看代码,感觉每次请求都创建client有点费劲,就把client提取出来,只初始化一次,再跑for循环请求测试,竟然成功了,即使没加time.sleep,也能正常返回200!
func GetClient() (client *http.Client) { if myClient != nil { return myClient } else { tlsCfg := &tls.Config{ InsecureSkipVerify: true, MaxVersion: tls.VersionTLS12, } tr := &http.Transport{ TLSClientConfig: tlsCfg, } client = &http.Client{ Transport: tr, } myClient = client return client } }
再google看下有没说明,刚输入golang http.client,后面就自动帮我补上复用两字了,看来有戏https://zhuanlan.zhihu.com/p/474206147。大概意思是http.Client会自动缓存连接,提高效率,本着不求甚解的精神,我默默的关闭了页面。
后来,当出现401后,我随手将golang项目停止服务后,fiddler模拟请求正常,curl、python都正常了,也就是golang程序占着茅坑导致其它应用同一个号都访问不了。这再次验证了所谓http.client复用的问题?
其实golang的http.Client里就有说明,只怪我读书少,没见识😢
// The Client's Transport typically has internal state (cached TCP // connections), so Clients should be reused instead of created as // needed. Clients are safe for concurrent use by multiple goroutines.
感觉如果golang踩坑太少,当用在关键项目上,出现问题后那场景真不堪设想!这篇文章写的比较乱,各位将就看吧!
HTTP Client复用:golang的http.Client应该复用,而不是每次请求都创建新的实例
连接池问题:频繁创建新连接可能导致服务器端连接数限制或认证状态异常
超时设置:务必为http.Client设置合理的超时时间,避免请求阻塞
资源占用:不正确使用http.Client可能导致连接资源被占用,影响其他客户端访问