分享
  1. 首页
  2. 文章

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

allenxguo · · 3790 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。


各类限频原理

网上有很多讲解限频原理以及限频原因的,限频常用在接口、服务的流量、并发上,主要是为了合理使用后端资源,防止后端被压垮,雪崩等等。

实现方法

这里使用使用go的ring(环形队列)实现滑动窗口

实现代码

package main
import (
 "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 = 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 = 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 = 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]

查看其中数组可以知道每一秒此时滑动窗口的限频值,以及变化。

压测试客户端输出

可以看出压测试服务在12.6秒进行了300次http请求其中有23次正确响应成功,限频测试ok

D:\Project\go\bin> .\hey.exe -c 6 -n 300
-q 6 -t 80 http://localhost:9090
Summary:
 Total: 12.6701 secs
 Slowest: 1.0163 secs
 Fastest: 0.0008 secs
 Average: 0.0789 secs
 Requests/sec: 23.6779
Response 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 secs
Details (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 secs
Status code distribution:
 [200] 23 responses
 [404] 277 responses

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:allenxguo

查看原文:Go实现滑动窗口限频及测试

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3790 次点击
1 回复 | 直到 2021年12月31日 16:33:10
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏