diff --git a/src/modules/collector/cache/cache.go b/src/modules/collector/cache/cache.go index 642dd0a387f7af26a198c3214d10a54f22a2573b..9cacc4d78d0a8efd9f73097edfe669bff1ee57df 100644 --- a/src/modules/collector/cache/cache.go +++ b/src/modules/collector/cache/cache.go @@ -5,12 +5,16 @@ import ( "time" "github.com/didi/nightingale/src/dataobj" + process "github.com/shirou/gopsutil/process" ) var MetricHistory *History +var ProcsCache *ProcessCache + func Init() { MetricHistory = NewHistory() + ProcsCache = NewProcsCache() } func NewHistory() *History { @@ -61,3 +65,51 @@ func (h *History) clean() { } } } + +type ProcessCache struct { + sync.RWMutex + Data map[int32]*process.Process +} + + +func NewProcsCache() *ProcessCache{ + pc := ProcessCache{ + Data: make(map[int32]*process.Process), + } + go pc.Clean() + return &pc +} + +func (pc *ProcessCache) Set(pid int32, p *process.Process){ + pc.Lock() + defer pc.Unlock() + pc.Data[pid] = p +} + +func (pc *ProcessCache) Get(pid int32)(*process.Process, bool){ + pc.RLock() + defer pc.RUnlock() + p, exists := pc.Data[pid] + return p, exists +} + +func (pc *ProcessCache) Clean() { + ticker := time.NewTicker(10 * time.Minute) + for { + select { + case <-ticker.C: + pc.clean() + } + } +} + +func (pc *ProcessCache) clean() { + pc.Lock() + defer pc.Unlock() + for pid, procs := range pc.Data { + running, _ := procs.IsRunning() + if !running{ + delete(pc.Data, pid) + } + } +} diff --git a/src/modules/collector/sys/procs/scheduler.go b/src/modules/collector/sys/procs/scheduler.go index 036f3e108ba57cf3efee8c09b3a56e07a6622822..1ef3563dd1722b2a359525779f8194fad6e7ae39 100644 --- a/src/modules/collector/sys/procs/scheduler.go +++ b/src/modules/collector/sys/procs/scheduler.go @@ -5,11 +5,11 @@ import ( "time" "github.com/toolkits/pkg/logger" - "github.com/toolkits/pkg/nux" - + process "github.com/shirou/gopsutil/process" "github.com/didi/nightingale/src/dataobj" "github.com/didi/nightingale/src/model" "github.com/didi/nightingale/src/modules/collector/sys/funcs" + "github.com/didi/nightingale/src/modules/collector/cache" "github.com/didi/nightingale/src/toolkits/identity" ) @@ -45,32 +45,74 @@ func (p *ProcScheduler) Stop() { } func ProcCollect(p *model.ProcCollect) { - ps, err := nux.AllProcs() + ps, err := process.Processes() if err != nil { logger.Error(err) return } - - pslen := len(ps) + var memUsedTotal uint64 = 0 + var memUtilTotal = 0.0 + var cpuUtilTotal = 0.0 + var items [] *dataobj.MetricValue cnt := 0 - for i := 0; i < pslen; i++ { - if isProc(ps[i], p.CollectMethod, p.Target) { + for _, procs := range ps { + if isProc(procs, p.CollectMethod, p.Target) { cnt++ + procCache, exists := cache.ProcsCache.Get(procs.Pid) + if !exists{ + cache.ProcsCache.Set(procs.Pid, procs) + procCache = procs + } + mem, err := procCache.MemoryInfo() + if err != nil { + logger.Error(err) + continue + } + memUsedTotal += mem.RSS + memUtil, err := procCache.MemoryPercent() + if err != nil { + logger.Error(err) + continue + } + memUtilTotal += float64(memUtil) + cpuUtil, err := procCache.Percent(0) + if err != nil { + logger.Error(err) + continue + } + cpuUtilTotal += cpuUtil } + + } - item := funcs.GaugeValue("proc.num", cnt, p.Tags) - item.Step = int64(p.Step) - item.Timestamp = time.Now().Unix() - item.Endpoint = identity.Identity + procNumItem := funcs.GaugeValue("proc.num", cnt, p.Tags) + memUsedItem := funcs.GaugeValue("proc.mem.used", memUsedTotal, p.Tags) + memUtilItem := funcs.GaugeValue("proc.mem.util", memUtilTotal, p.Tags) + cpuUtilItem := funcs.GaugeValue("proc.cpu.util", cpuUtilTotal, p.Tags) + items = []*dataobj.MetricValue{procNumItem, memUsedItem, memUtilItem, cpuUtilItem} + now := time.Now().Unix() + for _, item := range items{ + item.Step = int64(p.Step) + item.Timestamp = now + item.Endpoint = identity.Identity + } - funcs.Push([]*dataobj.MetricValue{item}) + funcs.Push(items) } -func isProc(p *nux.Proc, method, target string) bool { - if method == "name" && target == p.Name { +func isProc(p *process.Process, method, target string) bool { + name, err := p.Name() + if err != nil { + return false + } + cmdlines, err := p.Cmdline() + if err != nil { + return false + } + if method == "name" && target == name { return true - } else if (method == "cmdline" || method == "cmd") && strings.Contains(p.Cmdline, target) { + } else if (method == "cmdline" || method == "cmd") && strings.Contains(cmdlines, target) { return true } return false