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()
}

问题出现在了 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()
}

后期预警

通过每小时检测一次服务器 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
相关推荐
热心市民lcj5 分钟前
Spring Boot 整合 Caffeine 本地缓存实战
spring boot·后端·缓存
Revolution619 分钟前
Nest.js 是什么:怎样用它写出第一个后端接口
后端·node.js·nestjs
Richard.Wong10 分钟前
qt生成dll供C#调用
开发语言·qt
aiopencode18 分钟前
SwiftUI Introspect生产环境完全指南:为什么它是安全可靠的选择
后端·ios
shengjk126 分钟前
x86架构发展史:从8086到x86-64,一文看懂40多年CPU指令集如何改变世界
后端
Immortal__y28 分钟前
php函数
开发语言·php
AI砖家31 分钟前
多商户多租户系统架构设计文档(Java版)
java·开发语言·系统架构·多租户·多商户
鱼子星_1 小时前
【C++】深入剖析list:list及其双向迭代器实现
开发语言·数据结构·c++·笔记·stl·list
脚踏实地皮皮晨2 小时前
003002004_WPF Panel 基类 官方类定义
开发语言·windows·算法·c#·wpf·visual studio
520拼好饭被践踏2 小时前
JAVA+Agent学习day25
java·开发语言·学习