Collect historic cpu and mem stats. Also, use actual values instead of formatted strings while returning to the client. The string formatting prevents values from being processed by the server or by the client without parsing it. This change will allow the values to be processed (eg. compute rolling-average over the lifetime of the minio server) and offloads the formatting to the client.
143 lines
3.8 KiB
Go
143 lines
3.8 KiB
Go
/*
|
|
* Minio Cloud Storage, (C) 2019 Minio, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package cpu
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// rollingAvg holds the rolling average of the cpu load on the minio
|
|
// server over its lifetime
|
|
var rollingAvg *Load
|
|
|
|
// cpuMeasureInterval is the interval of time between two
|
|
// measurements of CPU load
|
|
const cpuLoadMeasureInterval = 5 * time.Second
|
|
|
|
// triggers the average load computation at server spawn
|
|
func init() {
|
|
rollingAvg = &Load{
|
|
Min: float64(0),
|
|
Max: float64(0),
|
|
Avg: float64(0),
|
|
}
|
|
var rollingSum float64
|
|
var cycles float64
|
|
go func() {
|
|
for {
|
|
time.Sleep(cpuLoadMeasureInterval)
|
|
cycles = cycles + 1
|
|
currLoad := GetLoad()
|
|
if rollingAvg.Max < currLoad.Max || rollingAvg.Max == 0 {
|
|
rollingAvg.Max = currLoad.Max
|
|
}
|
|
if rollingAvg.Min > currLoad.Min || rollingAvg.Min == 0 {
|
|
rollingAvg.Min = currLoad.Min
|
|
}
|
|
rollingSum = rollingSum + currLoad.Avg
|
|
rollingAvg.Avg = rollingSum / cycles
|
|
}
|
|
}()
|
|
}
|
|
|
|
const (
|
|
// cpuLoadWindow is the interval of time for which the
|
|
// cpu utilization is measured
|
|
cpuLoadWindow = 200 * time.Millisecond
|
|
|
|
// cpuLoadSampleSize is the number of samples measured
|
|
// for calculating cpu utilization
|
|
cpuLoadSampleSize = 3
|
|
|
|
// endOfTime represents the end of time
|
|
endOfTime = time.Duration(1<<63 - 1)
|
|
)
|
|
|
|
// Load holds CPU utilization % measured in three intervals of 200ms each
|
|
type Load struct {
|
|
Avg float64 `json:"avg"`
|
|
Max float64 `json:"max"`
|
|
Min float64 `json:"min"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
type counter struct{}
|
|
|
|
// GetHistoricLoad returns the historic CPU utilization of the current process
|
|
func GetHistoricLoad() Load {
|
|
return *rollingAvg
|
|
}
|
|
|
|
// GetLoad returns the CPU utilization of the current process
|
|
// This function works by calcualating the amount of cpu clock
|
|
// cycles the current process used in a given time window
|
|
//
|
|
// This corresponds to the CPU utilization calculation done by
|
|
// tools like top. Here, we use the getclocktime with the
|
|
// CLOCK_PROCESS_CPUTIME_ID parameter to obtain the total number of
|
|
// clock ticks used by the process so far. Then we sleep for
|
|
// 200ms and obtain the the total number of clock ticks again. The
|
|
// difference between the two counts provides us the number of
|
|
// clock ticks used by the process in the 200ms interval.
|
|
//
|
|
// The ratio of clock ticks used (measured in nanoseconds) to number
|
|
// of nanoseconds in 200 milliseconds provides us the CPU usage
|
|
// for the process currently
|
|
func GetLoad() Load {
|
|
vals := make(chan time.Duration, 3)
|
|
wg := sync.WaitGroup{}
|
|
for i := 0; i < cpuLoadSampleSize; i++ {
|
|
cpuCounter, err := newCounter()
|
|
if err != nil {
|
|
return Load{
|
|
Error: err.Error(),
|
|
}
|
|
}
|
|
wg.Add(1)
|
|
go func() {
|
|
start := cpuCounter.now()
|
|
time.Sleep(cpuLoadWindow)
|
|
end := cpuCounter.now()
|
|
vals <- end.Sub(start)
|
|
wg.Done()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
sum := time.Duration(0)
|
|
max := time.Duration(0)
|
|
min := (endOfTime)
|
|
for i := 0; i < cpuLoadSampleSize; i++ {
|
|
val := <-vals
|
|
sum = sum + val
|
|
if val > max {
|
|
max = val
|
|
}
|
|
if val < min {
|
|
min = val
|
|
}
|
|
}
|
|
close(vals)
|
|
avg := sum / 3
|
|
return Load{
|
|
Avg: toFixed4(float64(avg)/float64(200*time.Millisecond)) * 100,
|
|
Max: toFixed4(float64(max)/float64(200*time.Millisecond)) * 100,
|
|
Min: toFixed4(float64(min)/float64(200*time.Millisecond)) * 100,
|
|
Error: "",
|
|
}
|
|
}
|