Go实现滑动窗口限频及测试

各类限频原理 网上有很多讲解限频原理以及限频原因的,限频常用在接口、服务的流量、并发上,主要是为了合理使用后端资源,防止后端被压垮,雪崩等等。
实现方法 这里使用使用go的ring(环形队列)实现滑动窗口
实现代码

package mainimport ( "fmt" "net" "os" "container/ring" "sync/atomic" "sync" "time" )var ( limitCount int = 10 // 6s限频 limitBucket int = 6 // 滑动窗口个数 curCount int32 = 0// 记录限频数量 head *ring.Ring// 环形队列(链表) )func main() { tcpAddr, err := net.ResolveTCPAddr("tcp4", "0.0.0.0:9090") //获取一个tcpAddr checkError(err) listener, err := net.ListenTCP("tcp", tcpAddr) //监听一个端口 checkError(err) defer listener.Close() // 初始化滑动窗口 head = ring.New(limitBucket) for i := 0; i < limitBucket; i++ { head.Value = https://www.it610.com/article/0 head = head.Next() } // 启动执行器 go func() { timer := time.NewTicker(time.Second * 1) for range timer.C { // 定时每隔1秒刷新一次滑动窗口数据 subCount := int32(0 - head.Value.(int)) newCount := atomic.AddInt32(&curCount, subCount)arr := [6]int{} for i := 0; i < limitBucket; i++ { // 这里是为了方便打印 arr[i] = head.Value.(int) head = head.Next() } fmt.Println("move subCount,newCount,arr", subCount, newCount,arr) head.Value = https://www.it610.com/article/0 head = head.Next() } }()for { conn, err := listener.Accept() // 在此处阻塞,每次来一个请求才往下运行handle函数 if err != nil { fmt.Println(err) continue } go handle(&conn) // 起一个单独的协程处理,有多少个请求,就起多少个协程,协程之间共享同一个全局变量limiting,对其进行原子操作。 } }func handle(conn *net.Conn) { defer (*conn).Close() n := atomic.AddInt32(&curCount, 1) //fmt.Println("handler n:", n) if n > int32(limitCount) { // 超出限频 atomic.AddInt32(&curCount, -1) // add 1 by atomic,业务处理完毕,放回令牌 (*conn).Write([]byte("HTTP/1.1 404 NOT FOUND\r\n\r\nError, too many request, please try again.")) } else { mu := sync.Mutex{} mu.Lock() pos := head.Prev() val := pos.Value.(int) val++ pos.Value = https://www.it610.com/article/val mu.Unlock() time.Sleep(1 * time.Second) // 假设我们的应用处理业务用了1s的时间 (*conn).Write([]byte("HTTP/1.1 200 OK\r\n\r\nI can change the world!")) // 业务处理结束后,回复200成功。 } }// 异常报错的处理 func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) } }

压测试 另外起一个终端,用golang的boom来做压测。要提前安装boom工具
go get github.com/rakyll/hey go install github.com/rakyll/hey

进行压测试:
PS D:\Project\go\bin> .\hey.exe -c 6 -n 300 -q 6 -t 80 http://localhost:9090

压测试输出
move subCount,newCount,arr 0 0 [0 0 0 0 0 0] move subCount,newCount,arr 0 0 [0 0 0 0 0 0] move subCount,newCount,arr 0 6 [0 0 0 0 0 6] move subCount,newCount,arr 0 10 [0 0 0 0 6 4] move subCount,newCount,arr 0 10 [0 0 0 6 4 0] move subCount,newCount,arr 0 10 [0 0 6 4 0 0] move subCount,newCount,arr 0 10 [0 6 4 0 0 0] move subCount,newCount,arr -6 4 [6 4 0 0 0 0] move subCount,newCount,arr -4 6 [4 0 0 0 0 6] move subCount,newCount,arr 0 10 [0 0 0 0 6 4] move subCount,newCount,arr 0 10 [0 0 0 6 4 0] move subCount,newCount,arr 0 10 [0 0 6 4 0 0] move subCount,newCount,arr 0 10 [0 6 4 0 0 0] move subCount,newCount,arr -6 4 [6 4 0 0 0 0] move subCount,newCount,arr -4 3 [4 0 0 0 0 3] move subCount,newCount,arr 0 3 [0 0 0 0 3 0] move subCount,newCount,arr 0 3 [0 0 0 3 0 0] move subCount,newCount,arr 0 3 [0 0 3 0 0 0] move subCount,newCount,arr 0 3 [0 3 0 0 0 0] move subCount,newCount,arr -3 0 [3 0 0 0 0 0] move subCount,newCount,arr 0 0 [0 0 0 0 0 0] move subCount,newCount,arr 0 0 [0 0 0 0 0 0]

查看其中数组可以知道每一秒此时滑动窗口的限频值,以及变化。
压测试客户端输出
【Go实现滑动窗口限频及测试】可以看出压测试服务在12.6秒进行了300次http请求其中有23次正确响应成功,限频测试ok
D:\Project\go\bin> .\hey.exe -c 6 -n 300 -q 6 -t 80 http://localhost:9090Summary: Total:12.6701 secs Slowest:1.0163 secs Fastest:0.0008 secs Average:0.0789 secs Requests/sec: 23.6779Response time histogram: 0.001 [1]| 0.102 [276]|■■■■■■■■■■■■■ ■■■■■■■■■■■■■■■■■■■■■■ ■■■■■ 0.204 [0]| 0.305 [0]| 0.407 [0]| 0.509 [0]| 0.610 [0]| 0.712 [0]| 0.813 [0]| 0.915 [0]| 1.016 [23]|■■■Latency distribution: 10% in 0.0012 secs 25% in 0.0014 secs 50% in 0.0016 secs 75% in 0.0019 secs 90% in 0.0071 secs 95% in 1.0016 secs 99% in 1.0162 secsDetails (average, fastest, slowest): DNS+dialup:0.0020 secs, 0.0008 secs, 1.0163 secs DNS-lookup:0.0012 secs, 0.0001 secs, 0.0150 secs req write:0.0000 secs, 0.0000 secs, 0.0001 secs resp wait:-67.5636 secs, -2902.3027 secs, 1.0014 secs resp read:0.0000 secs, 0.0000 secs, 0.0003 secsStatus code distribution: [200] 23 responses [404] 277 responses

    推荐阅读