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


各类限频原理

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

实现方法

这里使用使用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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容