首页 > 代码库 > 【原创】请避免GO语言中的携程空跑(CPU突然激增)

【原创】请避免GO语言中的携程空跑(CPU突然激增)

其实GO语言从1.6版本开始非常不错了,GC性能优化非常到位,并且各种并行设计比从新实现一套C++版本的确是方便不少。

语言包也很多,库也相对稳定,完全可以适用于生产环境。

 

本文主要是给刚刚入门新手注意一个携程空跑的问题,因为这种问题可能在C++中也遇到过,只是一些代码书写习惯导致。

 

首先来看一段代码:

func (c *WSConn) processHandler() {
	for {
		select {
		case message, ok := <-c.processMsg: // 处理数据包
			if !ok {
				break
			}
			Call(message.MsgHead.Id, c, message.MsgContext, int(message.MsgHead.Msglen))
		}
	}
}

以上代码是用于处理一个WEBSOCKET的二进制消息后转换为指定处理信息的行为。

但是有没有同学发现有什么问题?但是这段代码的确有问题,因为当连接销毁后会导致processHandler这个携程空跑,CPU完全占满,当你有多个连接出现这种问题后整台服务器就会爆掉。

首先processMsg是一个channel,这里如果连接关闭了会同时关闭掉这个channel,首先我们知道select本身会等待channel,这样是不会消耗CPU的,就像C中的select函数一样,本身是不消耗的(使用不当的略过)。

但是当channel关闭后,整个携程本因直接销毁,但是代码中的break导致select无限循环跑,程序出现空跑现象,这里的break是相对于select而言的,所以看上去没毛病可跑起来毛病很大。

所以如果当出现空跑或GO语言某个携程CPU激增,可以去查看是不是哪个channel和select在无限循环。

所以正确的代码是:

func (c *WSConn) processHandler() {
	for {
		select {
		case message, ok := <-c.processMsg: // 处理数据包
			if !ok {
				return // 这里必须强制结束携程
			}
			Call(message.MsgHead.Id, c, message.MsgContext, int(message.MsgHead.Msglen))
		}
	}
}

我的排错方法是使用http的一种性能分析方式

下面是详细代码:

main.go

package main

import (
	"log"
	"runtime"

	"net/http" // http包引入
	_ "net/http/pprof" // 性能分析包引入

)

func main() {
	// 设置并行运行
	runtime.GOMAXPROCS(2)

	logger.SetLogName("testserver.log")

    
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}() // 此处建立http专用的性能分析端口

	webSock := knlWebsocket.Create(":88", "null")
	webSock.Listen()

}

  以上代码仅供抛砖引玉,无法通过编译,注意注释内的代码。

看代码很简单,import 2个包:

import (
	"net/http" // http包引入
	_ "net/http/pprof" // 性能分析包引入

)
然后main函数中加入代码:
go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}() // 此处建立http专用的性能分析端口

 我在这里加入了一个携程来做性能分析,是因为我本身有自己的主处理逻辑,所以必须使用携程。

   完成以上代码添加后,直接编译启动程序,访问地址:http://localhost:6060/debug/pprof/,然后就可以进行愉快的性能分析了

 

【原创】请避免GO语言中的携程空跑(CPU突然激增)