• go 内存泄露


    事件回顾

    • 9.15号晚18点服务端发版
    • 9.16号晚21点监控显示自发版后服务器 TCP_alloc 指标一路飙升至40K(如图)

    内存泄露

    问题分析

    看到 tcp_alloc 指标异常,初步怀疑有tcp连接创建后未关闭,应该是上次发版写了什么代码导致的。回顾此次发版清单,问题应该出现在了daemon服务心跳上报上。

    daemon服务是一个业务监控服务,通过监听etcd中注册的服务状态变更,向企业微信用户发送消息提醒。在此之前由于消息提醒协程panic异常退出,导致Ad服务停止后消息提醒未正常发出,造成生产P3事故。本次发版通过增加每3秒心跳保活机制,双重保证在服务中断后,消息提醒能正常发出(代码如下)

    func KeepAliveWithMp() {
    	var keepAliveWithMpUrl, _ = config.String("keepAliveWithMpUrl")
    	if keepAliveWithMpUrl == "" {
    		return
    	}
    	ticker := time.NewTicker(3 * time.Second)
    	for range ticker.C {
    		_, _ = http.Get(keepAliveWithMpUrl + "?runningKey=" + getRunningKey())
    	}
    	ticker.Stop()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    问题出现在了 http.Get 请求发出后未读取响应主体,也没有关闭此次tcp连接,导致一直在创建新的tcp连接,一个get请求会创建两个goroutine,导致内存泄露。

    此处有两种修复方式,一是在http请求完了之后主动关闭此次连接释放资源,另一种则是读取响应主体后复用改链接

    代码修复

    // 方式一
    func KeepAliveWithMp() {
    	var keepAliveWithMpUrl, _ = config.String("keepAliveWithMpUrl")
    	if keepAliveWithMpUrl == "" {
    		return
    	}
    	ticker := time.NewTicker(3 * time.Second)
    	for range ticker.C {
    		resp, _ := http.Get(keepAliveWithMpUrl + "?runningKey=" + getRunningKey())
    		_ = resp.Body.Close()
    	}
    	ticker.Stop()
    }
    
    
    // 方式二
    func KeepAliveWithMp() {
    	var keepAliveWithMpUrl, _ = config.String("keepAliveWithMpUrl")
    	if keepAliveWithMpUrl == "" {
    		return
    	}
    	ticker := time.NewTicker(3 * time.Second)
    	for range ticker.C {
    		resp, _ := http.Get(keepAliveWithMpUrl + "?runningKey=" + getRunningKey())
    		_,_ = ioutil.ReadAll(resp.Body)
    	}
    	ticker.Stop()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    后期预警

    通过每小时检测一次服务器 TCP_alloc 指标,当超过30K 后发送短信提醒,避免再次出现该异常。

    // TCP_alloc 指标查看方式
    cat /proc/net/sockstat 
    
    sockets: used 3621
    TCP: inuse 2833 orphan 5 tw 1792 alloc 3354 mem 489
    UDP: inuse 9 mem 4
    UDPLITE: inuse 0
    RAW: inuse 0
    FRAG: inuse 0 memory 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    Roson的Qt之旅 #113 QML布局之StackLayout(堆栈布局)
    YOLOV8目标检测——最全最完整模型训练过程记录
    吃透Chisel语言.34.Chisel进阶之硬件生成器(三)——利用面向对象编程特性:以Ticker为例
    聚观早报 | 百度糯米发布下线公告;零跑汽车获港交所上市批准
    8.Mobilenetv2网络代码实现
    强化学习笔记
    Cobalt Strike(五)会话管理
    前端面试题之——兼容篇
    Redis有哪些数据结构?分别有哪些典型的应用场景?
    DFS例题(n皇后问题)C++(Acwing)
  • 原文地址:https://blog.csdn.net/qq_39647045/article/details/133165065