golang 实现qps 滑动窗口流量统计

思想就是用循环数组来实现整个结构。其中numSlots用来表示滑动窗口的持续时间,比如30s,间隔时间是1s执行一次,这个可以用goroutine起一个定时器实现。计数就用golang atmic 实现,这个具有原子性。每一次步进就是
(curIndex+1)%numSlots
下边就是相关代码。

package service

import (
    "fmt"
    "sync/atomic"
    "time"
)

// 滑动窗口实时 QPS 可视化示例
type QpsService struct {
    Slots     []uint64
    NumSlots  int
    Interval  time.Duration
    CurrIndex int
}

/*
时间段,间隔时间
*/
func NewQPSCounter(duration, interval time.Duration) *QpsService {
    numSlots := int(duration / interval)
    counter := &QpsService{
        Slots:    make([]uint64, numSlots),
        NumSlots: numSlots,
        Interval: interval,
    }
    //每个1s 往下一个槽
    go func() {
        ticker := time.NewTicker(interval)
        defer ticker.Stop()
        for range ticker.C {
            counter.nextSlot()
        }
    }()
    return counter
}

// 把指针移动到下一个时间槽,如果到末尾就从头开始。
// 因为我们已经切换到新的时间槽了,开始统计新的时间段的请求数
func (q *QpsService) nextSlot() {
    q.CurrIndex = (q.CurrIndex + 1) % q.NumSlots
    atomic.StoreUint64(&q.Slots[q.CurrIndex], 0)
}

// 对当前分片值加1
func (q *QpsService) Add() {
    atomic.AddUint64(&q.Slots[q.CurrIndex], 1)
}

// 每一个分片中的数量
func (q *QpsService) Count() (sum uint64) {
    for i := 0; i < q.NumSlots; i++ {
        sum += atomic.LoadUint64(&q.Slots[i])
    }
    return sum
}

// 快照
func (q *QpsService) Snapshot() []uint64 {
    snapshot := make([]uint64, q.NumSlots)
    for i := 0; i < q.NumSlots; i++ {
        snapshot[i] = atomic.LoadUint64(&q.Slots[i])
    }
    return snapshot
}

// 绘制 ASCII QPS 图
func (q *QpsService) drawQPSChart(slots []uint64) {
    // 计算最大槽数量
    maxSlots := uint64(1)
    for _, v := range slots {
        if v > maxSlots {
            maxSlots = v
        }
    }
    //顶部 h=6 → 只看到槽3满了水 → 打印 █
    //h=5 → 槽3仍然有水 → █
    //h=4 → 槽3仍然有水 → █
    //h=3 → 槽2和槽3有水 → █ █
    //h=2 → 槽2、槽3、槽4有水 → █ █ █
    //h=1 → 所有槽都有水 → █ █ █ █
    height := 10 // 图表高度
    for h := height; h >= 1; h-- {
        //每一个slots
        for _, v := range slots {
            //把请求数映射到柱状图高度
            barHeight := v * uint64(height) / maxSlots
            if barHeight >= uint64(h) {
                fmt.Print("█")
            } else {
                fmt.Print(" ")
            }
        }
        fmt.Println()
    }
    fmt.Println(fmt.Sprint(slots)) // 打印 slot 数值
    fmt.Println()
}

评论

(= ̄ω ̄=)··· 暂无内容!

回复

邮箱