github.com/whoyao/protocol@v0.0.0-20230519045905-2d8ace718ca5/utils/cpu.go (about)

     1  package utils
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/frostbyte73/core"
     7  	"go.uber.org/atomic"
     8  
     9  	"github.com/whoyao/protocol/logger"
    10  )
    11  
    12  // This object returns cgroup quota aware cpu stats. On other systems than Linux,
    13  // it falls back to full system stats
    14  
    15  type platformCPUMonitor interface {
    16  	getCPUIdle() (float64, error)
    17  	numCPU() int
    18  }
    19  
    20  type CPUStats struct {
    21  	idleCPUs atomic.Float64
    22  	platform platformCPUMonitor
    23  
    24  	updateCallback  func(idle float64)
    25  	warningThrottle core.Throttle
    26  	closeChan       chan struct{}
    27  }
    28  
    29  func NewCPUStats(updateCallback func(idle float64)) (*CPUStats, error) {
    30  	p, err := newPlatformCPUMonitor()
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	c := &CPUStats{
    36  		platform:        p,
    37  		warningThrottle: core.NewThrottle(time.Minute),
    38  		updateCallback:  updateCallback,
    39  		closeChan:       make(chan struct{}),
    40  	}
    41  
    42  	go c.monitorCPULoad()
    43  
    44  	return c, nil
    45  }
    46  
    47  func (c *CPUStats) GetCPUIdle() float64 {
    48  	return c.idleCPUs.Load()
    49  }
    50  
    51  func (c *CPUStats) NumCPU() int {
    52  	return c.platform.numCPU()
    53  }
    54  
    55  func (c *CPUStats) Stop() {
    56  	close(c.closeChan)
    57  }
    58  
    59  func (c *CPUStats) monitorCPULoad() {
    60  	ticker := time.NewTicker(time.Second)
    61  	defer ticker.Stop()
    62  
    63  	for {
    64  		select {
    65  		case <-c.closeChan:
    66  			return
    67  		case <-ticker.C:
    68  			idle, err := c.platform.getCPUIdle()
    69  			if err != nil {
    70  				logger.Errorw("failed retrieving CPU idle", err)
    71  				continue
    72  			}
    73  
    74  			c.idleCPUs.Store(idle)
    75  			idleRatio := idle / float64(c.platform.numCPU())
    76  
    77  			if idleRatio < 0.1 {
    78  				c.warningThrottle(func() { logger.Infow("high cpu load", "load", 1-idleRatio) })
    79  			}
    80  
    81  			if c.updateCallback != nil {
    82  				c.updateCallback(idle)
    83  			}
    84  		}
    85  	}
    86  }