golang系統性能監控初探


系統服務(這里不局限於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))
	}
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM