脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Golang官方限流器库实现限流示例详解

Golang官方限流器库实现限流示例详解

2022-11-10 10:52jiaxwu Golang

这篇文章主要为大家介绍了Golang官方限流器库使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

在翻Golang官方库的过程中,发现一个有趣的库golang.org/x/time ,里面只有一个类rate,研究了一下发现它是一个限流器,实现了很多的功能,当然它的核心原理并不复杂,也就是令牌桶算法。

令牌桶算法的原理是:令牌桶会不断地把令牌添加到桶里,而请求会从桶中获取令牌,只有拥有令牌地请求才能被接受。因为桶中可以提前保留一些令牌,所以它允许一定地突发流量通过。

例子

下面是限流算法常见的写法,首先判断是否有令牌,如果有就通过,否则直接失败。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
import (
    "fmt"
    "time"
    "golang.org/x/time/rate"
)
func main() {
        // 每0.1秒生成一个令牌,也就是一秒10个令牌,最大保留令牌上限10
    l := rate.NewLimiter(rate.Every(time.Second/10), 10)
    for i := 0; i < 10; i++ {
        go func(i int) {
            for {
                                // 判断是否有令牌,如果有就输出
                if l.Allow() {
                    fmt.Printf("allow %d\n", i)
                }
                                // 每0.5秒请求一次
                time.Sleep(time.Second / 2)
            }
        }(i)
    }
    time.Sleep(time.Second * 10)
}

上面的rate.Every(time.Second/10)会返回一个Limit类型,代表每秒生成多少个令牌。

这个库还提供了另外一种写法,等待直到有令牌为止(或超时):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
    l := rate.NewLimiter(rate.Every(time.Second/10), 100)
    for i := 0; i < 10; i++ {
        go func(i int) {
            for {
                                // 等待直到有令牌
                if err := l.Wait(context.TODO()); err != nil {
                } else {
                    fmt.Printf("allow %d\n", i)
                }
                time.Sleep(time.Second / 2)
            }
        }(i)
    }
    time.Sleep(time.Second * 10)
}

这样在某些场景下我们可以让请求等待一会,而不是直接失败。

还有一个更加特殊的请求令牌方式,也就是先预留令牌,到指定时间不再需要去获取令牌,直接执行操作即可:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
    l := rate.NewLimiter(rate.Every(time.Second/10), 10)
    for i := 0; i < 10; i++ {
        go func(i int) {
            for {
                                // 先预留令牌
                if r := l.Reserve(); r.OK() {
                                        // 休眠直到令牌生效
                    time.Sleep(r.Delay())
                    fmt.Printf("allow %d\n", i)
                }
                time.Sleep(time.Second / 2)
            }
        }(i)
    }
    time.Sleep(time.Second * 10)
}

当然,如果预留的令牌不想使用了,也可以使用r.Cancel()归还已预留的令牌。

上面的Allow()、Wait()、Reserve()都是一次消耗一个令牌,其实都有对应的AllowN()、WaitN()、ReserveN()方法,一次消耗N个令牌,这样就可以根据任务消耗的资源灵活的消耗令牌。

实现

不管我们从Allow()、Wait()还是Reserve()进去,最终都会进入到reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation 方法:

首先在进入方法的时候,会先处理两种特殊情况:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    // 如果无限生成令牌,则直接返回
    if lim.limit == Inf {
    return Reservation{
        ok:        true,
        lim:       lim,
        tokens:    n,
        timeToAct: now,
    }
    // 如果不会生成令牌,则在初始令牌里面拿,直到拿完为止
} else if lim.limit == 0 {
    var ok bool
    if lim.burst >= n {
        ok = true
        lim.burst -= n
    }
    return Reservation{
        ok:        ok,
        lim:       lim,
        tokens:    lim.burst,
        timeToAct: now,
    }
}

然后重新计算当前有多少令牌,减去要消耗的令牌:

?
1
2
3
4
5
6
7
8
9
10
11
    // 计算当前有多少令牌
now, last, tokens := lim.advance(now)
// 减去要消耗的N个令牌
tokens -= float64(n)
    // 如果剩余令牌为负数,那么计算一下要等待多久才能拿到令牌
    var waitDuration time.Duration
if tokens < 0 {
    waitDuration = lim.limit.durationFromTokens(-tokens)
}
    // 判断请求是否成功,maxFutureReserve代表最大可以等待的时间,也就是请求能否接收拿到令牌需要等待的时间
    ok := n <= lim.burst && waitDuration <= maxFutureReserve

余下的代码就是更新限流器的状态。

小结

可以看到这个令牌桶限流器实现的功能非常的丰富,如果需要令牌桶限流器,可以优先考虑使用这个实现。

以上就是Golang官方限流器库使用示例详解的详细内容,更多关于Golang官方限流器库的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/7130890097947312158

延伸 · 阅读

精彩推荐
  • Golanggolang使用grpc+go-kit模拟oauth认证的操作

    golang使用grpc+go-kit模拟oauth认证的操作

    这篇文章主要介绍了golang使用grpc+go-kit模拟oauth认证的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    鹿灏楷silves8212021-05-30
  • Golanggolang中单向channel的语法介绍

    golang中单向channel的语法介绍

    通过消息来共享数据是golang的一种设计哲学,channel则是这种哲理的体现。下面这篇文章主要给大家介绍了关于golang中单向channel语法的相关资料,文中通过...

    鱼儿5022020-05-08
  • GolangGolang实现断点续传功能

    Golang实现断点续传功能

    这篇文章主要为大家详细介绍了Golang实现断点续传、复制文件功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    开心码农1号6202022-07-26
  • GolangGolang语言的多种变量声明方式与使用场景详解

    Golang语言的多种变量声明方式与使用场景详解

    Golang当中的变量类型和C/C++比较接近,一般用的比较多的也就是int,float和字符串,下面这篇文章主要给大家介绍了关于Golang语言的多种变量声明方式与使用场景...

    frank10322022-09-03
  • GolangGoland 断点调试Debug的操作

    Goland 断点调试Debug的操作

    这篇文章主要介绍了Goland 断点调试Debug的操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    天朗气清jim7772021-06-02
  • GolangGo 实现HTTP中间人代理的操作

    Go 实现HTTP中间人代理的操作

    这篇文章主要介绍了Go 实现HTTP中间人代理的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    order_c9032021-06-11
  • GolangGo语言二维数组的传参方式

    Go语言二维数组的传参方式

    这篇文章主要介绍了Go语言二维数组的传参方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    hello_bravo_4192021-06-04
  • GolangGolang开发gRPC服务入门介绍

    Golang开发gRPC服务入门介绍

    这篇文章主要介绍了Golang开发gRPC服务,Golang开发gRPC应用程序的套路也已经很清晰,这篇文章就来做一个简单的介绍,算是入门,需要的朋友可以参考下...

    波斯马11302022-09-25