• 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
  • 相关阅读:
    5.RabbitMQ高级特性
    若依(RuoYi )权限管理设计
    Typescript给定一个由key值组成的数组keys,返回由数组项作为key值组成的对象
    7年阿里测试经验之谈 —— 用UI自动化测试实现元素定位
    .NET集成IdGenerator生成分布式全局唯一ID
    渗透测试中的前端调试(上)
    阿里M8每天肝到凌晨,竟是只为一份文档把分布式到微服务讲清楚
    Appium+python+unittest搭建UI自动化框架
    华为云创新中心&黑湖科技:将智能制造进行到底
    小命令,大世界
  • 原文地址:https://blog.csdn.net/qq_39647045/article/details/133165065