服务压测的心路历程——解决数据竞争与空闲连接问题

NVIDIA-SMI has failed because it couldn‘t communicate with the NVIDIA driver. 最全解决方案!详细! 真的折磨了我整整一天,从网上的资料到GPT都翻遍了,终于是解决了!以下方案希望能帮助到你们。 阅读详情

背景

最近,我们的服务需要对外开放。在此之前,我们需要进行压测。由于这是一个跨部门的项目,我们没有足够的上游资源,因此必须自行模拟(Mock)服务进行压测。通过使用 go build -race 编译,我们可以在压力测试中发现数据竞争问题。

问题

隐藏的数据竞争

首先,我们进行了100左右的并发压测,并修复了一个数据竞争的问题。这个问题可能非常普遍,因为它很容易犯。其原因可能是 Golang 不建议采用此种方式实现并发。

func foo() chan interface{} {
    out := make(chan interface{})
    in := make(chan interface{})
    go func() {
        defer close(out)
        go func() {
            for v := range in {
                out <- do(v)
            }    
        }()
        if err := produce(in); err != nil {
            out <- err
        } // stuck work
        close(in)
    }()
    return out 
}

这里的 produce 接收一个 chan<- interfaceP{} 并返回 error ,是阻塞的,但是 foo 是异步返回 chan interface{} ,因此需要起一个协程去作为 out 的生产者,还需要起一个协程作为in 的消费者。

这段代码有无问题?是有的,这里面存在一个数据竞争的 bug ,不知道大伙能不能发现。其实只要关注读写两种操作就行了, 这里面的 in 读是通过 range 实现的,并且在生产者 produce(in) 返回后使用了 close(in) 正确关闭了,因此 in 的读写操作是正确的。再来看 out 读写操作,out 的读写操作出现三个协程里面,一是 foo 返回后的协程,二是外面写入 out <- err 的协程,三是写入 out <- do(v) 的协程,这里约定外面的协程只有for range 读取操作,主要考虑 foo 里面的两个协程。两个写入操作:

  • 第12行的 out <- err ,这里的 out 一定不是 nil 并且一定不是 closed ,因此可以正常写入。

  • 第8行的 out <- do(v) ,这里的 out 有可能是 closed ,当 close(in) 后,out <- do(v) 还会继续消费在 in 队列中最后一个元素,因此我们并不能保证 defer close(out) 发生在这个消费动作之后。所以,在这种情况下,尝试向已关闭的 out 写入数据会导致 PANIC。

服务端未正常处理空闲连接

在100并发压测正常后,将并发数提升至4000,这时候出现了问题,服务报错:

accept tcp 0.0.0.0:8001: accept4: too many open files; retrying in 10ms

看起来是文件句柄数过多,使用 watch -n 1 ss -s 监测系统的 socket 数一直高居不下,并且到2000多就开始出现上述错误,后面通过 ulimit -n 4096 将最大文件数提高,但依旧很快到达上限。

这里初步怀疑是客户端在连接时没有正常关闭,也就是 resp.Body.Close() 没有正常执行,但是未发现问题,后面将Mock的下游服务和客户端分离部署,分别查看 socket 连接数,结果显示 Mock 服务端结果依旧异常,客户端的 socket 连接数正常。因此怀疑是 Mock 服务端在结束请求后,连接并没有回收,而是在躺在连接池中,经过与 ChatGPT的对话后,疑问解开了,对话内容总结如下:

Q: 在客户端使用 resp.Body.Close() 后,golang http 服务端会正确断开连接,释放资源吗?

GPT: 在Golang的http客户端中,当你调用resp.Body.Close()后,客户端会正确关闭与服务端的连接,释放连接占用的资源。但是在某些情况下,HTTP协议的特性可能会导致连接保持,即使客户端已经关闭了该连接。

Q: 但是系统显示建立的连接还是存在的,及时客户端已经使用了resp.Body.Close()

GPT: 即使客户端正确关闭了响应体,TCP 连接可能确实会在服务端短暂地保持打开状态。这种情况下,可能是服务端在使用 Keep-alive 策略。

ChatGPT YYDS!也就是空闲连接占用,但是客户端没有复用连接,或者说服务端来不及回收空闲连接,net/http 的默认参数为:

var DefaultTransport RoundTripper = &Transport{
        Proxy: ProxyFromEnvironment,
        DialContext: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
                DualStack: true,
        }).DialContext,
        ForceAttemptHTTP2:     true,
        MaxIdleConns:          100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
}

默认的 IdleConnTimeout 为 90s ,在压测程序过程中根本来不及回收,因此需要将服务端的 IdleConnTimeout 设置得短一些。

mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
	// do something
})
server := &http.Server{
    Addr:        *host,
    Handler:     mux,
    IdleTimeout: 10 * time.Second,
}
fmt.Println(server.ListenAndServe())

然后通过 ss -s 监测压测过程中的连接数情况,嗯,一切正常!

ss -s
// 初始状态
Total: 1165 (kernel 11337)
TCP:   806 (estab 20, closed 772, orphaned 0, synrecv 0, timewait 18/0), ports 4260

Transport Total     IP        IPv6
*         11337     -         -
RAW       2         1         1
UDP       5         3         2
TCP       34        30        4
INET      41        34        7
FRAG      0         0         0

// 测试中
Total: 1188 (kernel 11337)
TCP:   816 (estab 29, closed 773, orphaned 0, synrecv 0, timewait 19/0), ports 4260

Transport Total     IP        IPv6
*         11337     -         -
RAW       2         1         1
UDP       5         3         2
TCP       43        39        4
INET      50        43        7
FRAG      0         0         0

// 测试后 测试客户端进程结束
Total: 1185 (kernel 11337)
TCP:   828 (estab 41, closed 774, orphaned 0, synrecv 0, timewait 20/0), ports 4260

Transport Total     IP        IPv6
*         11337     -         -
RAW       2         1         1
UDP       5         3         2
TCP       54        50        4
INET      61        54        7
FRAG      0         0         0

结束服务端的程序后,estab 状态的 socket 数量回归正常。

结论

总之,在程序压测时,使用 go build -race 能够很好的帮助我们发现数据竞争问题。而在处理模拟的 HTTP 服务时,需要注意客户端关闭请求体并不意味着服务端立即释放资源。服务端要正确处理请求后,才能释放相关资源。为了解决这个问题,我们需要适当地设置连接空闲时间 IdleConnTimeout。

SIwave仿真手册——信号完整性仿真之S参数的提取(二) 上一篇简单了讲述了软件的各个操作界面,本次博客更新关于的仿真,因为第二章完整的内容比较多,所以分成好几篇博客进行分享。 阅读详情

相关推荐

Windows10系统添加虚拟网卡(Microsoft Loopback Adapter)的方法

1、按下win + R键,在其中输入:hdwwiz,然后 (enter) 2、点击下一步即可 3、其次选择【网络适配器】,然后点击【下一步】。 4、在厂商处选择Microsoft,网络适配器处选择【Microsoft Loopback Adapter】选项 ,点击【下一步】。 5、通过上面几个步骤的设置后,一块虚拟网卡的安装设置就算是完成了。 ...

一个懒鬼的博客 2万+

golang net/http 超时机制完全手册

英文原始出处:The complete guide to Go net/http timeouts, 作者:Filippo Valsorda 当用Go写HTTP的服务器和客户端的时候,超时处理总是最易犯错和最微妙的地方之一。错误可能来自很多地方,一个错误可能等待很长时间没有结果,直到网络故障或者进程挂起。 HTTP是一个复杂的、多阶段(multi-stage)协议,所以没有一个放之四海而皆准的超时解决方案,比如一个流服务、一个JSON API和一个Comet服务对超时的需求都不相同, 往往默认值不是..

whatday的专栏 2894

浮点数精度陷阱全解析:从IEEE 754到深度学习部署选型

在计算机数值计算中,浮点数是表示带小数数字的标准方式,但它的存储规则决定了精度和边界天生存在限制。许多开发者都曾遭遇过0.1+0.2不等于0.3的尴尬,或是模型训练中loss变成NaN的困扰,这些问题的根源往往指向IEEE 754标准中的规格化、非规格化表示及舍入规则。理解浮点数的底层原理,不仅能避开直接的比较判断陷阱,还能指导我们在不同场景下做出合理的技术选型。从C语言的printf到Python的repr,从嵌入式串口打印到深度学习中的FP32、FP16、BF16、TF32格式,浮点数知识贯穿底层开发和

weixin_34314962的博客 463

net/http transport 参数讲解

对go的标准库net/http中transport的各个参数的讲解

qq_36915686的博客 1330

企业微信群机器人Webhook配置全攻略:从创建到发送消息的完整流程

本文详细解析了企业微信群机器人的Webhook配置全流程,从创建机器人、获取关键Webhook地址,到使用代码发送文本、Markdown等多种消息类型。内容涵盖安全加固、错误处理及与CI/CD等系统集成的实战指南,帮助企业快速构建高效、稳定的内部通知系统。

ik678901的博客 1783

Python实战:超分辨率图像质量评估全攻略——PSNR、SSIM、LPIPS与NIQE的代码实现与优化

本文详细介绍了在Python中实现超分辨率图像质量评估的完整方案,涵盖PSNR、SSIM、LPIPS和NIQE四大核心指标。通过模块化代码设计、多进程并行处理和GPU加速优化,帮助开发者构建高效、可扩展的评估系统,并提供了实战避坑指南与结果解读方法。

weixin_29053073的博客 474

python MySQLdb 长时间idle connection timeout, ping()

By default, MySQL-5.0 does not automatically reconnect. mysql连接如果长时间idle的话,(时间:默认为8小时),会自动断开,而且不会为原连接自动恢复。 在python中,如果应用程序某个模块使用了持久化的db链接,则失效后,继续使用,会报错: 2006,MySQL server has gone away 解决办法:...

watershitter 617

从零做出可发布的2D冒险游戏!Unity全流程实战指南:从资源搭建到打包发布,新手也能跟着做

项目配置→资源导入→核心模块开发→关卡设计→调试→打包发布;玩家控制→敌人AI→道具系统→UI系统→游戏管理器→音效系统;新手开发重点:先实现核心玩法(移动、跳跃、攻击),再补充UI、音效、存档等辅助功能;调试是关键:每实现一个功能就测试,避免最后一堆bug难以定位。从零做一款完整的2D冒险游戏,能快速提升你的Unity综合能力——不仅能掌握C#编程、2D物理、UI系统,还能理解游戏开发的完整流程。先做出基础版,再慢慢迭代优化,你会发现游戏开发并没有想象中那么难。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1176

go HTTP Client大量长连接保持(自定义client设置及源码简单分析)

一,问题起因        线上server to server的服务,出现大量的TIME_WAIT。用netstat发现,不断的有连接在建立,没有保持住连接。抓TCP包确认request和response中的keepalive都已经设置,但是每个TCP连接处理6次左右的http请求后,就被关闭。        就处理该问题的过程中,查看了一下http client的部分源码。 二,HTTP

kdpujie的专栏 3万+

通过 CoovaChilli 和 FreeRADIUS 搭建一个公共 Wi-Fi 的认证系统,实现用户连接到 Wi-Fi 后通过 Captive Portal(强制登录页面)进行认证

要实现固定设备连接的功能,你可以通过MAC 地址认证来限制哪些设备可以通过 FreeRADIUS 进行身份验证和连接。你可以在 FreeRADIUS 的users文件中直接配置允许的设备的 MAC 地址,也可以通过数据库存储和查询设备信息,进一步提高管理效率。通过这些方法,你可以灵活地控制哪些设备能够访问你的 Wi-Fi 网络。

m0_69943017的博客 2647

Docker(八):Docker Compose(容器编排) 管理多容器应用—实战案例演示

🟢 Docker(八):Docker Compose(容器编排) 管理多容器应用—实战案例演示

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 6万+

go net/http Client使用——长连接客户端的使用

go net/http Client使用总结 Client数据结构 // A Client is an HTTP client. Its zero value (DefaultClient) is a // usable client that uses DefaultTransport. // // The Client's Transport typically has inte

路漫漫其修远兮 2万+

基于深度学习的X光安检危险品检测系统(YOLOv8 + UI界面 + 数据集)

随着全球安全形势的变化,机场、车站、地铁等场所的安检工作日益重要。X光安检系统已经成为现代公共安全领域中不可或缺的部分,能够有效地检测物品的内部结构,识别潜在的危险品。然而,传统的X光图像分析方法仍然依赖人工操作,检测效率较低,并且易受到人为因素的影响。近年来,深度学习技术的应用在图像分析领域取得了显著的突破,特别是在目标检测任务中,YOLO(You Only Look Once)模型因其高效性和精度,成为了许多目标检测任务的首选模型。

YOLO 1060

在职字节4年,一位28岁女软件测试工程师的心路历程

我想说的第一条就是要学会深入思考,总结沉淀,这是我觉得最重要也是最有意义的一件事。先来说深入思考。在测试员这个圈子里,常能听到一些言论:我这个工作一点技术含量都没有,这能让我学到什么东西?抛开一部分调侃和戏谑的论调不谈,这可能确实是一部分同学的真实想法,至少曾经的我,就这么认为过。后来随着工作经验的积累,加上和一些高level的同学交流探讨之后,我发现这个想法其实是非常错误的。之所以出现没什么可学的这样的看法,基本上是思维懒惰的结果。

HUA1211的博客 276

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C语言内存池实战:从malloc原理到自定义实现(附完整代码)

本文深入剖析了标准malloc函数在高频小内存分配场景下的性能瓶颈,如系统调用、锁竞争与内存碎片。针对这些痛点,文章详细介绍了C语言内存池的实现原理与设计思路,通过预分配和链表管理技术,实现了远超malloc的分配效率。文末提供了完整的工业级内存池实现代码,并附有性能对比测试,为开发者优化实时系统、网络服务器等高性能应用提供了实战方案。

weixin_29018335的博客 385

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

从超时到硬核优化:全链路性能诊断与实战调优指南

在软件工程实践中,性能优化是提升系统响应能力和用户体验的核心环节。其原理在于通过系统化的方法识别和消除瓶颈,涉及算法效率、资源管理和架构设计等多个层面。优化的技术价值在于能够显著降低延迟、提高吞吐量,并增强系统的可扩展性与稳定性。常见的应用场景包括高并发接口、大数据量处理以及实时系统等。本次探讨聚焦于立体化性能诊断,运用APM工具与Profiler进行深度剖析,并结合JVM调优与缓存策略,系统解决从数据库访问到内存管理的复合型性能问题,实现从被动修复到主动优化的思维跃迁。

weixin_30323631的博客 300

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Nginx配置文件优化指南:如何通过调整参数提升服务器性能(含常见错误排查)

本文深入探讨了Nginx配置文件的核心优化策略,旨在通过精细调整连接处理、事件模型、缓冲区及缓存等关键参数,显著提升服务器在高并发场景下的性能与稳定性。文章不仅提供了针对worker_processes、epoll、sendfile等指令的实战调优建议,还涵盖了常见错误(如502/504错误)的排查方法论,帮助开发者将配置从通用模板转化为贴合业务的高效系统。

weixin_29243063的博客 100

创业第一个月总结

    前段时间决定出来创业后,写了一篇简单的博文“技术变成客户才值钱”来勉励自己,想不到有这么多朋友深有同感,也有部分朋友问我创业进行得如何。首先谢谢大家的支持,我想只要我有些空闲时间,我会将自己创业过程中的一些好的案例和心得在这里写出来,希望与大家一起前进。    实际上,与许多朋友们一样,在开始出来创业时,不断在网上查如何创业、怎样创业、创业要注意什么等相关内容,但搜来搜去,很难找到自己心中...

weixin_34377919的博客 85

我从外包辞职了,10000小时后,走进字节跳动拿了offer

前言: 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力+幸运,但如果你不够幸运,那就只能拉高努力的占比。 2020年7月,我有幸成为了字节跳动的一名Java后端开发,正如标题所说,我从外包辞职了,10000小时后,走进字节跳动拿下了offer。相信同行都清楚,从外包进大厂有多难,运气之余,也离不开我自己的脚踏实地,所幸每踏出的一步都留下了厚厚的脚印。 快速入手通道:(点这里)下载!诚意满满!!! Java面试精选题、架构实战文档传送门:VX:Nice.

weixin_49723683的博客 975

从外包辞职,苦熬3个月后,拿了字节跳动的 offer

前言: 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力+幸运,但如果你不够幸运,那就只能拉高努力的占比。 2021 年 7 月,我有幸成为了字节跳动的一名 Java 后端开发,正如标题所说,我从外包辞职了,10000 小时后,走进字节跳动拿下了 offer。相信同行都清楚,从外包进大厂有多难,运气之余,也离不开我自己的脚踏实地,所幸每踏出的一步都留下了厚厚的脚印。 A:字节跳动 IES 互娱面试经历还原(抖音后台) <1> 字节跳动抖音 4 面..

pao12375321的博客 1297

GitHub访问破百万!字节2021年Java程序员面试指导已疯传

前言: 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力+幸运,但如果你不够幸运,那就只能拉高努力的占比。 2020年11月,我有幸成为了字节跳动的一名Java后端开发,正如标题所说,我从外包辞职了,10000小时后,走进字节跳动拿下了offer。相信同行都清楚,从外包进大厂有多难,运气之余,也离不开我自己的脚踏实地,所幸每踏出的一步都留下了厚厚的脚印。 下文内容涉及到:面试题(含答案)+学习笔记+电子书籍+学习视频,只需点击传送门即可!! A:字节..

bieber007的博客 849

嘻嘻!刚拿到毕业证的我,拿着offer走进了字节跳动,美滋滋

前言: 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力+幸运,但如果你不够幸运,那就只能拉高努力的占比。 2021年7月,我有幸成为了字节跳动的一名Java后端开发,正如标题所说,大专刚毕业的我,走进字节跳动拿下了offer。相信同行都清楚,从外包进大厂有多难,运气之余,也离不开我自己的脚踏实地,所幸每踏出的一步都留下了厚厚的脚印。 下文内容涉及到:面试题(含答案)+学习笔记+电子书籍+学习视频! A:字节跳动IES互娱面试经历还原(抖音后台) &l

javassb的博客 386

最新面经出炉:双非一本学历裸辞奋战4个月,从外包跳进字节,给到29k*14薪!

前言: 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力+幸运,但如果你不够幸运,那就只能拉高努力的占比。 2021年4月,我有幸成为了字节跳动的一名Java后端开发,正如标题所说,我从外包辞职了,10000小时后,走进字节跳动拿下了offer。相信同行都清楚,从外包进大厂有多难,运气之余,也离不开我自己的脚踏实地,所幸每踏出的一步都留下了厚厚的脚印。 需要资料的小伙伴,点赞加收藏,关注我之后添加小助理vx:1426687161 即可获取免费下载方式 .

Java圈全能架构师的博客 1330

从POC到EXP:绕过NX/PIE、vtable劫持与堆利用的实战解析

在软件安全领域,漏洞利用(Exploit)的构建是将安全研究转化为实际攻防能力的关键。其核心原理在于控制程序执行流,而现代操作系统普遍部署的NX(数据执行保护)和ASLR/PIE(地址空间布局随机化)等防护机制,旨在通过隔离代码与数据、随机化内存布局来增加利用难度。为了绕过这些防护,面向返回的编程(ROP)技术应运而生,它通过复用程序已有的代码片段(gadget)来构造执行链,实现了在不可执行内存区域下的代码复用攻击。这项技术的价值在于,它使得在存在基础防护的复杂环境下实现稳定利用成为可能,广泛应用于从系统

chupu2979的博客 494

外包裸辞10000小时后,字节跳动四面成功收割offer

前言: 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力+幸运,但如果你不够幸运,那就只能拉高努力的占比。 2020年7月,我有幸成为了字节跳动的一名Java后端开发,正如标题所说,我从外包辞职了,10000小时后,走进字节跳动拿下了offer。相信同行都清楚,从外包进大厂有多难,运气之余,也离不开我自己的脚踏实地,所幸每踏出的一步都留下了厚厚的脚印。 下文内容涉及到:面试题(含答案)+学习笔记+电子书籍+学习视频,免费打包分享,私信【外包】即可获取。 .

weixin_59330273的博客 678

我不想干外包了,连续两次跳槽,我有幸成为了字节跳动的一名Java后端开发~

前言 没有绝对的天才,只有持续不断的付出。对于我们每一个平凡人来说,改变命运只能依靠努力+幸运,但如果你不够幸运,那就只能拉高努力的占比。 2021年7月,我有幸成为了字节跳动的一名Java后端开发,正如标题所说,我从外包辞职了,10000小时后,走进字节跳动拿下了offer。相信同行都清楚,从外包进大厂有多难,运气之余,也离不开我自己的脚踏实地,所幸每踏出的一步都留下了厚厚的脚印。 下文内容涉及到:面试题(含答案)+学习笔记+电子书籍+学习视频,免费打包分享,查看文末即可获取。 A:字

jinggege795的博客 1253
上一篇: 流式输出场景下的字符串替换——一次颇具挑战性的需求
下一篇: 看似合理的io.Reader 接口
Corleo
博客等级 码龄11年 120粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值