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

目录
  • 前言
  • 例子
  • 实现
  • 小结

前言

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

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

例子

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

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类型,代表每秒生成多少个令牌。

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

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)
}

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

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

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 方法:

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

        // 如果无限生成令牌,则直接返回
        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,
		}
	}

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

        // 计算当前有多少令牌
	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官方限流器库的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go实现各类限流的方法

    前 言 在开发高并发系统时,我们可能会遇到接口访问频次过高,为了保证系统的高可用和稳定性,这时候就需要做流量限制,你可能是用的 Nginx 这种来控制请求,也可能是用了一些流行的类库实现.限流是高并发系统的一大杀器,在设计限流算法之前我们先来了解一下它们是什么. 限 流 限流的目的是通过对并发访问请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务.排队或等待.降级等处理.通过对并发(或者一定时间窗口内)请求进行限速来保护系统,一旦达到限制速率则拒绝服务(定

  • golang接口IP限流,IP黑名单,IP白名单的实例

    增加中间件 可以选择普通模式和LUA脚本模式,建议选择普通模式,实际上不需要控制的那么精确. package Middlewares import ( "github.com/gin-gonic/gin" "strconv" "time" "voteapi/pkg/app/response" "voteapi/pkg/gredis" "voteapi/pkg/util" ) const

  • go实现一个分布式限流器的方法步骤

    目录 1. 接口定义 2. LocalCounterLimiter 3. LocalTokenBucketLimiter 4. RedisCounterLimiter 5. RedisTokenBucketLimiter 项目中需要对 api 的接口进行限流,但是麻烦的是,api 可能有多个节点,传统的本地限流无法处理这个问题.限流的算法有很多,比如计数器法,漏斗法,令牌桶法,等等.各有利弊,相关博文网上很多,这里不再赘述. 项目的要求主要有以下几点: 支持本地/分布式限流,接口统一 支持多种限

  • 详解Golang实现请求限流的几种办法

    简单的并发控制 利用 channel 的缓冲设定,我们就可以来实现并发的限制.我们只要在执行并发的同时,往一个带有缓冲的 channel 里写入点东西(随便写啥,内容不重要).让并发的 goroutine在执行完成后把这个 channel 里的东西给读走.这样整个并发的数量就讲控制在这个 channel的缓冲区大小上. 比如我们可以用一个 bool 类型的带缓冲 channel 作为并发限制的计数器. chLimit := make(chan bool, 1) 然后在并发执行的地方,每创建一个新

  • golang高并发限流操作 ping / telnet

    需求 当需要同时ping/telnet多个ip时,可以通过引入ping包/telnet包实现,也可以通过go调用cmd命令实现,不过后者调用效率较差,所以这里选择ping包和telnet包 还有就是高并发的问题,可以通过shell脚本或者go实现高并发,所以我选择的用go自带的协程实现,但是如果要同时处理1000+个ip,考虑到机器的性能,需要ratelimit控制开辟的go协程数量,这里主要写一下我的建议和淌过的坑 ping 参考链接: https://github.com/sparrc/go

  • go redis实现滑动窗口限流的方式(redis版)

    之前给大家介绍过单机当前进程的滑动窗口限流 , 这一个是使用go redis list结构实现的滑动窗口限流 , 原理都一样 , 但是支持分布式 原理可以参考之前的文章介绍 func LimitFreqs(queueName string, count uint, timeWindow int64) bool { currTime := time.Now().Unix() length := uint(ListLen(queueName)) if length < count { ListPus

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

    目录 前言 例子 实现 小结 前言 在翻Golang官方库的过程中,发现一个有趣的库golang.org/x/time ,里面只有一个类rate,研究了一下发现它是一个限流器,实现了很多的功能,当然它的核心原理并不复杂,也就是令牌桶算法. 令牌桶算法的原理是:令牌桶会不断地把令牌添加到桶里,而请求会从桶中获取令牌,只有拥有令牌地请求才能被接受.因为桶中可以提前保留一些令牌,所以它允许一定地突发流量通过. 例子 下面是限流算法常见的写法,首先判断是否有令牌,如果有就通过,否则直接失败. packa

  • golang官方嵌入文件到可执行程序的示例详解

    前言 在go官方出嵌入文件的方法前我在网上找过,并且自己还研究过,虽然没有问题,但是既然官方支持还是用起来吧. 看了下go源码embed/embed.go很简单,并且看embed/internal/embedtest/embed_test.go就知道如何使用. 示例程序 嵌入文件直接访问 原理是使用go:embed标签来完成.下面是直接读取文件内容,需要注意下面几点. 文件不是utf8编码时,输出内容为中文会乱码. 测试过嵌入文件只能为源码文件同级目录和子目录下的文件,试过其他目录的绝对路径或相

  • Go语言框架快速集成限流中间件详解

    目录 前言 分布式版 简介 算法 实现 注意 单机版 简介 算法 实现 结语 前言 在我们的日常开发中, 常用的中间件有很多, 今天来讲一下怎么集成限流中间件, 它可以很好地用限制并发访问数来保护系统服务, 避免系统服务崩溃, 资源占用过大甚至服务器崩溃进而影响到其他应用! 分布式版 简介 通常我们的服务会同时存在多个进程, 也就是负载来保证服务的性能和稳定性, 那么就需要走一个统一的限流, 这个时候就需要借助我们的老朋友-redis, 来进行分布式限流; 算法 漏桶算法 即一个水桶, 进水(接

  • Springboot使用redis进行api防刷限流过程详解

    这篇文章主要介绍了Springboot使用redis进行api防刷限流过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 api限流的场景 限流的需求出现在许多常见的场景中 秒杀活动,有人使用软件恶意刷单抢货,需要限流防止机器参与活动 某api被各式各样系统广泛调用,严重消耗网络.内存等资源,需要合理限流 淘宝获取ip所在城市接口.微信公众号识别微信用户等开发接口,免费提供给用户时需要限流,更具有实时性和准确性的接口需要付费. api限流实

  • Go编程库Sync.Pool用法示例详解

    目录 场景 用法 创建 GET & PUT 优化 Log 函数 性能测试 场景 go 如果频繁地创建.销毁对象(比如 http 服务的 json 对象,日志内容等),会对 GC 造成压力.比如下面的 Log 函数,在高并发情况下,需要频繁地创建和销毁 buffer. func Log(w io.Writer, key, val string) { b := new(bytes.Buffer) // 按一定的格式打印日志,这一段不是重点 b.WriteString(time.Now().UTC()

  • Java微服务Filter过滤器集成Sentinel实现网关限流过程详解

    目录 Gateway-过滤器Filter 局部路由过滤器 使用局部过滤器 全局过滤器 使用全局过滤器 集成Sentinel实现网关限流 网关限流 API分组限流 Gateway-过滤器Filter 过滤器就是在请求的传递过程中,对请求和响应做一些手脚. 在Gateway中, Filter的生命周期只有两个:“pre”和“post”". .PRE:这种过滤器在请求被路由之前调用.我们可利用这种过滤器实现身份验证.在集群中选择请求的微服务.记录调试信息等. .POST:这种过滤器在路由到微服务以后执

  • Nginx源码研究之nginx限流模块详解

    高并发系统有三把利器:缓存.降级和限流: 限流的目的是通过对并发访问/请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页).排队等待(秒杀).降级(返回兜底数据或默认数据): 高并发系统常见的限流有:限制总并发数(数据库连接池).限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数).限制时间窗口内的平均速率(nginx的limit_req模块,用来限制每秒的平均速率): 另外还可以根据网络连接数.网络流量.CPU或内存负载等来限流. 1.限流算法 最

  • java秒杀之redis限流操作详解

    最近写到了一个秒杀的功能模块,为了保证高并发情况下不会宕机,要从多方面去考虑,当前的限流操作只是其中的一个方面,具体操作如下. 导入所需依赖 <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.targ

  • React SSR 中的限流案例详解

    目录 为什么要限流 令牌桶算法 当对 React 应用进行页面加载或 SEO 优化时,我们一般绕不开 React SSR.但 React SSR 毕竟涉及到了服务端,有很多服务端特有的问题需要考虑,而限流就是其中之一. 所谓限流,就是当我们的服务资源有限.处理能力有限时,通过对请求或并发数进行限制从而保障系统正常运行的一种策略.本文会通过一个简单的案例来说明,为什么服务端需要进行限流. 为什么要限流 如下所示是一个简单的 nodejs 服务端项目: const express = require

  • 使用VitePress搭建及部署vue组件库文档的示例详解

    目录 安装vitepress 目录结构 文档首页 配置 导航栏配置 侧边栏 部署到GitHub Pages 每个组件库都有它们自己的文档.所以当我们开发完成我们自己的组件库必须也需要一个组件库文档.如果你还不了解如何搭建自己的组件库可以看这里->从零搭建Vue3组件库.看完这篇文章你就会发现原来搭建和部署一个组件库文档是那么的简单.当然部署也不需要你有自己的服务器,你只要有github即可.由于我们的组件库还没有完成,所以下面就以element-plus作为示例来搭建一个文档吧. 安装vitep

随机推荐