记一次pprof排查CPU吃满问题| 青训营笔记

277 阅读1分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天

这是我参与「第五届青训营 」伴学笔记创作活动不水的第 1 天

在写极简抖音的聊天模块时,频繁使用到channel和select-case.在第一次尝试运行时,我的clash崩了,浏览器也崩了,我以为只是普普通通的内存吃满,毕竟浏览器开了20+页面,我的轻薄本带不动时家常便饭.但是,但是,我发现任务管理器的数据:CPU100%,内存70%.我意识到这可不是内存的问题,再往下翻main.go占CPU60%.我想到我参考的代码中有多处select被我改了,我怀疑是自己改错了.于是我打开了pprof......

首先,gin框架使用pprof需要导入包,注册路由:

package main

import (
 "github.com/gin-contrib/pprof"
 "github.com/gin-gonic/gin"
)

func main() {
  router := gin.Default()
  pprof.Register(router)
  router.Run(":8080")
}

使用命令,go tool pprof http://localhost:8080/debug/pprof/profile,在pprof命令行下, 打入"top",打入"png": profile001.png 问题代码是这样的:如果channel没有东西的话,ok返回false,而!ok直接continue,那么相当于进入一个for死循环......这样cpu就吃满了.

for {
	select{
	case msgContent,ok := <-c.MsgChan:
		if !ok {
			continue
		}
                msgData := S2CMsgData{
                    FromUserId: c.UserId,
                    MsgContent: string(msgContent),
                }
		msg, _ := json.Marshal(msgData)
		_ = c.Conn.WriteMessage(websocket.TextMessage, msg)
	}
}

于是,我改成了:

for {
	select{
	case msgContent:= <-c.MsgChan:
                msgData := S2CMsgData{
                    FromUserId: c.UserId,
                    MsgContent: string(msgContent),
                }
		msg, _ := json.Marshal(msgData)
		_ = c.Conn.WriteMessage(websocket.TextMessage, msg)
	}
}

top,png: profile002.png 淦,依然吃满...而且主要开销在json上,我就很奇怪,难道说依然是死循环了,而且死循环的同时走了一遍序列化???,我最后把代码改成:

for {
	select{
	case msgContent,ok := <-c.MsgChan:
		if !ok {
			_ = c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
			return
		}
		msgData := S2CMsgData{
			FromUserId: c.UserId,
			MsgContent: string(msgContent),
		}
		msg, _ := json.Marshal(msgData)
		_ = c.Conn.WriteMessage(websocket.TextMessage, msg)
	}
}

top,png:

profile003.png 这个start函数是我的监听ws的函数,合理. 终于cpu降到了10%,危机解除.