系统服务(这里不局限于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)) } }
有疑问加站长微信联系(非本文作者)