golang系统性能监控初探

weijiaen · · 5231 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

系统服务(这里不局限于golang写的后台服务,也包括c++,java等后台语言)需要考虑的两个问题

1. 系统的资源使用情况(cpu利用率,内存分配情况等,runtime和syscall都提供了支持,这个是系统内部性质,往往是设计系统资源问题,需要在设计的时候慎重考虑)

2. 系统的服务情况(就是服务时延,这个是client可以直接感知的,往往是client最关注的,决定了服务的qps)

     提前评估系统的资源消耗是很重要的,在公司里面,能提供选择的机器类型是很多种的。比如腾讯,往往动则上亿qq号的量,很多时候需要在内存中cache用户的实时信息,如果内存评估不对,后面如果要进行机器迁移,则比较麻烦。另外,cpu的数目太少,则并发性弱,影响服务的性能,特别是在一台机器上部署了多个服务后,抢cpu,抢内存的情况则格外严重,并且,如果把io密集程序跟cpu密集程序放到一起,io密集程序还可能会把耗内存的cpu程序给挤到swap中去导致服务出现间歇性的大量失败。

 

我最近在用golang写一个server。压力测试过程却发现反应比较慢,但是由于设计的方案,中间的操作都是串行的,无法知道在哪个操作消耗了比较多时间。

一开始想到的是打log。但是单个请求又是很快的,于是想到如下方案 (by 魏加恩 本文地址http://www.cnblogs.com/weijiaen/p/3970466.html)

在调用每个函数的时候,统计该函数的时耗,然后利用channel把同一个函数调用发送到同一个地方,利用map进行累计统计(这里可以更近一步,比如统计每个worker甚至每个services的状态,包括最长请求时间,最短请求时间,平均消耗等等,如果加上runtime还可以记录其他的运行相关信息)。

一开始在思考是否有一个类似c++的static变量的东西,好在golang的闭包就支持有状态的行为,而且比static更进一步,闭包的每次调用都能得到一个新的局部变量实体,所以,并发的时耗,在不同的地方,调用就可以得到不同的结果。

 

直接上代码,关注点,系统资源利用统计,系统的服务时延统计,至于后续问题,比如何时log到文件等,以后有时间再补充进来。 

func preReportRusage(interval time.Duration) func(usage1 *syscall.Rusage, utilProf *SystemUtilProf) int {
	var lastUtime int64
	var lastStime int64
	var waitInterval time.Duration = interval

	a := func(usage1 *syscall.Rusage, utilProf *SystemUtilProf) int {
		if err := syscall.Getrusage(syscall.RUSAGE_SELF, usage1); err == nil {
			utime := usage1.Utime.Sec*1000000000 + usage1.Utime.Usec
			stime := usage1.Stime.Sec*1000000000 + usage1.Stime.Usec

			utilProf.userCPUUtil = float64(utime-lastUtime) * 100 / float64(waitInterval) //用户cpu时间
			utilProf.sysCPUUtil = float64(stime-lastStime) * 100 / float64(waitInterval)  //系统cpu时间
			utilProf.memUtil = uint64(usage1.Maxrss * 1024)                               //系统内存使用情况
			utilProf.memStats = runtime.MemStats{}

			runtime.ReadMemStats(&utilProf.memStats)

			lastUtime = utime
			lastStime = stime

			return kSuccess
		} else {
			return kFail
		}
	}

	return a
}


func preReportStatus(funcName string) func(reportType int, input chan<- *RequestProf) {
	var startTime = time.Now()
	var reportFunc = funcName

	a := func(reportType int, input chan<- *RequestProf) {
		consumeTime := int64(time.Now().Sub(startTime) / 1000)
		requestProf := &RequestProf{
			apiName:      reportFunc,
			consumeTime:  consumeTime,
			invokeTimes:  1,
			successCount: 0,
			errorCount:   0,
		}

		if reportType == kSuccess {
			requestProf.successCount = 1
		} else {
			requestProf.errorCount = 1
		}

		input <- requestProf
	}

	return a
}

var requestProfChan = make(chan *RequestProf,500)
var requestProfMap = make(map[string]*RequestProf)

func funcA() error {
	report := preReportStatus("funcA")

	err := funcB()

	if err != nil {
		report(kFail, requestProfChan)
		return errors.New("funcA error.")
	}

	report(kSuccess, requestProfChan)
	return nil
}

func funcC() {
     rusageReport := preReportRusage(time.Minute)
	//print rusage status...
	usage1 := &syscall.Rusage{}
	utilProf := &SystemUtilProf{}

	if result := rusageReport(usage1, utilProf); result == kSuccess {
		content = fmt.Sprintf("%3.2f,%3.2f,%s,%s,%s,%s\n",
		utilProf.userCPUUtil, utilProf.sysCPUUtil, toH(utilProf.memUtil),
		toH(utilProf.memStats.HeapSys), toH(utilProf.memStats.HeapAlloc), toH(utilProf.memStats.HeapIdle))
	}
}

  

本文来自:博客园

感谢作者:weijiaen

查看原文:golang系统性能监控初探

5231 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传