我们在做云原生调度技术调研的时候,为了做实验获取一些数据,需要编写一个demo, 支持动态模拟cup使用率和内存使用,所以用go开发了这么一个web程序。
package main
import (
"fmt"
"net/http"
"runtime"
"strconv"
"sync/atomic"
"time"
)
// 单个cpu利用率,[0~100]
var cpuUsage atomic.Int64
// 内存占用,单位Mb
var memoryUsage atomic.Int64
var memoryUsageBuf []byte
func init() {
cpuUsage.Store(0)
memoryUsage.Store(0)
cpuUsageTask()
}
func main() {
http.HandleFunc("/cpu", handleCpuUsgApi)
http.HandleFunc("/memory", handleMemoryUsgApi)
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("Error starting server:", err)
}
}
func handleCpuUsgApi(w http.ResponseWriter, r *http.Request) {
usage := r.URL.Query().Get("usage")
v, err := strconv.ParseInt(usage, 10, 64)
if err != nil || v < 0 || v > 100 {
w.WriteHeader(500)
w.Write([]byte("invalid step value"))
return
}
cpuUsage.Store(v)
w.WriteHeader(200)
w.Write([]byte("ok"))
}
// 不生效多调几次
func handleMemoryUsgApi(w http.ResponseWriter, r *http.Request) {
usage := r.URL.Query().Get("usage")
v, err := strconv.ParseInt(usage, 10, 64)
if err != nil || v < 0 {
w.WriteHeader(500)
w.Write([]byte("invalid step value"))
return
}
updateMemoryUsage(int(v))
w.WriteHeader(200)
w.Write([]byte("ok"))
}
func updateMemoryUsage(usageMb int) {
mb := usageMb * 1024 * 1024
memoryUsageBuf = nil
runtime.GC()
time.Sleep(200 * time.Millisecond)
memoryUsageBuf = make([]byte, mb)
}
func cpuUsageTask() {
go func() {
for {
usage := float64(cpuUsage.Load()) / float64(100)
Compute(usage)
}
}()
}
// Compute 使用cpu占用率达到目标值
// usage [0, 1], CPU利用率百分比
func Compute(usage float64) {
// 一个总的CPU利用率的统计周期为1000毫秒
var t = 1000.0
// 总时间转换为纳秒
var r int64 = 1000 * 1000
totalNanoTime := t * (float64)(r)
// 计算时间,纳秒
runTime := totalNanoTime * usage
// 休眠时间,纳秒
sleepTime := totalNanoTime - runTime
startTime := time.Now().UnixNano()
// 运行
for float64(time.Now().UnixNano())-float64(startTime) < runTime {
}
// 休眠
time.Sleep(time.Duration(sleepTime) * time.Nanosecond)
}
其中Compute方法就是模拟消耗cpu,参数usage是模拟的目标使用率,取值为0~1,例如0.1表示10%。可以通过top命令验证。 这个值不会很准确,但非常接近。例如想要模拟10%的cpu使用率,可能实际在9.8%左右。
handleMemoryUsgApi方法则是模拟消耗内存,有个问题就是可能需要重复调用两次才会生效。 先给memoryUsageBuf赋值为空,然后gc,再new新的对象,是考虑了,假如先前占用了500m, 现在想更新到800m,如果不先释放500m内存,短时间内就会占用1300m的内容,可能会触达limits。