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
推荐阅读
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- MybatisPlus使用queryWrapper如何实现复杂查询
- python学习之|python学习之 实现QQ自动发送消息
- 孩子不是实现父母欲望的工具——林哈夫
- opencv|opencv C++模板匹配的简单实现
- Node.js中readline模块实现终端输入
- java中如何实现重建二叉树
- 人脸识别|【人脸识别系列】| 实现自动化妆
- paddle|动手从头实现LSTM
- pytorch|使用pytorch从头实现多层LSTM