限流算法,基于go的gRPC 实现的

目录

一、单机限流

1、令牌桶算法

3、固定窗口限流算法

4、滑动窗口

二、集群限流

1、分布式固定窗口 (基于redis)

2、分布式滑动窗口


一、单机限流

1、令牌桶算法

令牌桶算法是当流量进入系统前需要获取令牌,没有令牌那么就要进行限流

这个算法是怎么实现的呢

  1. 定义一个后台协程按照一定的频率去产生token

  2. 后台协程产生的token 放到固定大小容器里面

  3. 有流量进入系统尝试拿到token,没有token 就需要限流了


type TokenBucketLimiter struct {token chan struct{}stop  chan struct{}
}
​
func NewTokenBucket(capactity int, timeInternal time.Duration) *TokenBucketLimiter {te := make(chan struct{}, capactity)stop := make(chan struct{})ticker := time.NewTicker(timeInternal)go func() {defer ticker.Stop()for {select {case <-ticker.C:select {case te <- struct{}{}:default:
​}case <-stop:return}}}()return &TokenBucketLimiter{token: te,stop:  stop,}
}
​
func (t *TokenBucketLimiter) BuildServerInterceptor() grpc.UnaryServerInterceptor {return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {select {case <-ctx.Done():err = ctx.Err()returncase <-t.token:return handler(ctx, req)case <-t.stop:err = errors.New("缺乏保护")return}}
}
​
func (t *TokenBucketLimiter) Stop() {close(t.stop)
}

3、固定窗口限流算法

什么是固定窗口限流算法

固定窗口限流算法(Fixed Window Rate Limiting Algorithm)是一种最简单的限流算法,其原理是在固定时间窗口(单位时间)内限制请求的数量。该算法将时间分成固定的窗口,并在每个窗口内限制请求的数量。具体来说,算法将请求按照时间顺序放入时间窗口中,并计算该时间窗口内的请求数量,如果请求数量超出了限制,则拒绝该请求。

优点:实现简单

缺点:对于瞬时流量没发处理,也就是临界问题,比如下图在20t前后,在16t以及26t有大量流量进来,在这10t中,已经超过了流量限制,没法限流

实现如下

type fixWindow1 struct {lastVistTime int64vistCount    int64interval     int64maxCount     int64
}
​
func NewfixWindow1(macCount int64) *fixWindow1 {t := &fixWindow1{maxCount: macCount,}return t
}
​
func (f *fixWindow1) FixWindow1() grpc.UnaryServerInterceptor {return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {current := time.Now().UnixNano()lasttime := atomic.LoadInt64(&f.lastVistTime)if lasttime+f.interval > current {if atomic.CompareAndSwapInt64(&f.lastVistTime, lasttime, current) {atomic.StoreInt64(&f.lastVistTime, current)atomic.StoreInt64(&f.maxCount, 0)}}count := atomic.AddInt64(&f.vistCount, 1)if count > f.maxCount {return gen.GetByIDResp{}, errors.New("触发限流")}return handler(ctx, req)}
}

4、滑动窗口

什么是滑动窗口算法:

滑动窗口限流算法是一种常用的限流算法,用于控制系统对外提供服务的速率,防止系统被过多的请求压垮。它将单位时间周期分为n个小周期,分别记录每个小周期内接口的访问次数,并且根据时间滑动删除过期的小周期。它可以解决固定窗口临界值的问题

type slideWindow struct {
timeWindow *list.Listinterval   int64maxCnt     intlock       sync.Mutex
}
​
func NewSlideWindow(interval time.Duration, maxCnt int) *slideWindow {t := &slideWindow{timeWindow: list.New(),interval:   interval.Nanoseconds(),maxCnt:     maxCnt,}return t
}
​
func (s *slideWindow) SlideWinowlimit() grpc.UnaryServerInterceptor {return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {s.lock.Lock()now := time.Now().UnixNano()// 快路径if s.timeWindow.Len() < s.maxCnt {resp, err = handler(ctx, req)s.timeWindow.PushBack(now)s.lock.Unlock()return}front := s.timeWindow.Front()for front != nil && front.Value.(int64)+s.interval < now {s.timeWindow.Remove(front)front = s.timeWindow.Front()}if s.timeWindow.Len() >= s.maxCnt {s.lock.Unlock()return &gen.GetByIdReq{}, errors.New("触发限流")}s.lock.Unlock()resp, err = handler(ctx, req)s.timeWindow.PushBack(now)return}
}

二、集群限流

下面是分布式限流,为啥是分布式限流,单机限流只能对单台服务器进行限流,没发对集权进行限流,需要用分布式限流来进行集权限流

1、分布式固定窗口 (基于redis)
type redisFix struct {
serName  stringinterVal intlimitCnt intredis    redis.Cmdable
}
​
//go:embed lua/fixwindow.lua
var lua_redis_fix string
​
func NewRedisFix(serName string, interval int, limitCnt int, redis redis.Cmdable) *redisFix {t := &redisFix{serName:  serName,interVal: interval,limitCnt: limitCnt,redis:    redis,}return t
}
​
func (r *redisFix) RedisFix() grpc.UnaryServerInterceptor {return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {res, err := r.limit(ctx)if err != nil {return &gen.GetByIDResp{}, err}if res {return &gen.GetByIdReq{}, errors.New("触发限流")}return handler(ctx, req)}
}
​
func (r *redisFix) limit(ctx context.Context) (res bool, err error) {keys := []string{r.serName}res, err = r.redis.Eval(ctx, lua_redis_fix, keys, r.interVal, r.limitCnt).Bool()return
}

lua

local key = KEYS[1]

local limitCnt = tonumber(ARGV[2])
local val = redis.call('get',key)
if val==false thenif limitCnt<1 thenreturn "true"elseredis.call('set',key,1,'PX',ARGV[1])return "false"end
elseif tonumber(val)<limitCnt thenredis.call('incr',key)return "false"
elsereturn "true"
end
2、分布式滑动窗口
//go:embed lua/slidewindow.lua

var slideWindLua string
​
type redisSlib struct {serverName stringinterVal   time.DurationmaxCnt     int64redis      redis.Cmdable
}
​
func NewRedisSlib(interval time.Duration, maxCnt int64, serverName string, clientCmd redis.Cmdable) *redisSlib {t := &redisSlib{serverName: serverName,interVal:   interval,maxCnt:     maxCnt,redis:      clientCmd,}return t
}
​
func (r *redisSlib) RedisSlibLimt() grpc.UnaryServerInterceptor {return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {limt, err := r.limt(ctx)if err != nil {return nil, err}if limt {return nil, errors.New("限流")}return handler(ctx, req)}
}
​
func (r *redisSlib) limt(ctx context.Context) (bool, error) {now := time.Now().UnixMilli()return r.redis.Eval(ctx, slideWindLua, []string{r.serverName}, r.interVal.Milliseconds(), r.maxCnt, now).Bool()
}

lua

local key = KEYS[1]
local window = tonumber(ARGV[1])
local maxCnt = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
​
--- 窗口的最小边界
local min = now-window
​
redis.call('ZREMRANGEBYSCORE',key,'-inf',min)
​
local cnt = redis.call('ZCOUNT',key,'-inf','+inf')
​
if cnt>=maxCnt thenreturn "true"
elseredis.call('ZADD',key,now,now)redis.call('PEXPIRE',key,window)return "false"
end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/211805.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python之html2text,清晰解读HTML内容!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python之html2text&#xff0c;清晰解读HTML内容&#xff0c;全文3900字&#xff0c;阅读大约10分钟。 HTML是Web开发中常见的标记语言&#xff0c;但有时我们需要将HTML内容…

关于 mapboxgl 的常用方法及效果

给地图标记点 实现效果 /*** 在地图上添加标记点* point: [lng, lat]* color: #83f7a0*/addMarkerOnMap(point, color #83f7a0) {const marker new mapboxgl.Marker({draggable: false,color: color,}).setLngLat(point).addTo(this.map);this.markersList.push(marker);},…

Ubuntu-rsyslog和systemd-journald日志服务

rsyslog日志服务 rsyslog作为传统的系统日志服务&#xff0c;把所有收集到的日志都记录到/var/log/目录下的各个日志文件中。 常见的日志文件如下&#xff1a; /var/log/messages 绝大多数的系统日志都记录到该文件 /var/log/secure 所有跟安全和认证授权等日志…

玩转Sass:掌握数据类型!

当我们在进行前端开发的时候&#xff0c;有时候需要使用一些不同的数据类型来处理样式&#xff0c;Sass 提供的这些数据类型可以帮助我们更高效地进行样式开发&#xff0c;本篇文章将为您详细介绍 Sass 中的数据类型。 布尔类型 在 Sass 中&#xff0c;布尔数据类型可以表示逻…

imutils库介绍及安装学习

目录 介绍 本机环境 安装 常用函数 使用方法 图像平移 图像缩放 图像旋转 骨架提取 通道转换 OPenCV版本的检测 综合测试 目录 介绍 本机环境 安装 常用函数 使用方法 图像平移 图像缩放 图像旋转 骨架提取 通道转换 OPenCV版本的检测 介绍 imutils 是一…

我不是DBA之慢SQL诊断方式

最近经常遇到技术开发跑来问我慢SQL优化相关工作&#xff0c;所以干脆出几篇SQL相关优化技术月报&#xff0c;我这里就以公司mysql一致的5.7版本来说明下。 在企业中慢SQL问题进场会遇到&#xff0c;尤其像我们这种ERP行业。 成熟的公司企业都会有晚上的慢SQL监控和预警机制。…

面试常问的dubbo的spi机制到底是什么?(下)

前文回顾 前一篇文章主要是讲了什么是spi机制&#xff0c;spi机制在java、spring中的不同实现的分析&#xff0c;同时也剖析了一下dubbo spi机制的实现ExtensionLoader的实现中关于实现类加载以及实现类分类的源码。 一、实现类对象构造 看实现类对象构造过程之前&#xff0c;先…

量子力学:探索微观世界的奇妙之旅

量子力学&#xff1a;探索微观世界的奇妙之旅 引言 在21世纪初&#xff0c;我们逐渐进入了一个以信息技术为主导的新时代。在这个时代&#xff0c;量子力学作为一门研究物质世界微观结构、粒子间相互作用以及能量与信息转换的基础科学&#xff0c;对我们的生活产生了深远的影响…

http和https的区别有哪些

目录 HTTP&#xff08;HyperText Transfer Protocol&#xff09; HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09; 区别与优势 应用场景 未来趋势 当我们浏览互联网时&#xff0c;我们经常听到两个常用的协议&#xff1a;HTTP&#xff08;HyperText Tra…

【MATLAB源码-第96期】基于simulink的光伏逆变器仿真,光伏,boost,逆变器(IGBT)。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. 光伏单元&#xff08;PV Cell&#xff09; 工作原理&#xff1a;光伏单元通过光电效应将太阳光转换为直流电。它们的输出取决于光照强度、单元温度和负载条件。Simulink建模&#xff1a;在Simulink中&#xff0c;光伏单元…

编程怎么学才能快速入门,分享一款中文编程工具快速学习编程思路,中文编程工具之分组框构件简介

一、前言&#xff1a; 零基础自学编程&#xff0c;中文编程工具下载&#xff0c;中文编程工具构件之扩展系统菜单构件教程 编程系统化教程链接 https://jywxz.blog.csdn.net/article/details/134073098?spm1001.2014.3001.5502 给大家分享一款中文编程工具&#xff0c;零基础…

【设计模式-4.3】行为型——责任链模式

说明&#xff1a;本文介绍设计模式中行为型设计模式中的&#xff0c;责任链模式&#xff1b; 审批流程 责任链模式属于行为型设计模式&#xff0c;关注于对象的行为。责任链模式非常典型的案例&#xff0c;就是审批流程的实现。如一个报销单的审批流程&#xff0c;根据报销单…

Matlab数学建模详解之发电机的最佳调度实现

&#x1f517; 运行环境&#xff1a;Matlab、Python &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&am…

从零构建属于自己的GPT系列3:模型训练2(训练函数解读、模型训练函数解读、代码逐行解读)

&#x1f6a9;&#x1f6a9;&#x1f6a9;Hugging Face 实战系列 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在PyCharm中进行 本篇文章配套的代码资源已经上传 从零构建属于自己的GPT系列1&#xff1a;数据预处理 从零构建属于自己的GPT系列2&#xff1a;模型训…

Java 中 char 和 Unicode、UTF-8、UTF-16、ASCII、GBK 的关系

Unicode、UTF-8、UTF-16、UTF-32、ASCII、GBK、GB2312、ISO-8859-1 它们之间是什么关系? 关于这几种字符编码的关系,经过各种资料研究,总结如下图(请右键在新标签页打开查看或者下载后使用看图工具放大查看): 我们应该从历史的顺序看待这些字符编码的由来: ASCII(早期…

Python之random和string库学习

一、random库 random是python中用来生存随机数的库。具体用法如下&#xff1a; 1、生成一个0到1随机浮点数 random.random() 2、生成一个a到b的随机浮点数 random.uniform(1,2) 3、生成一个a到b之间的整数 random.randint(a,b) 4、随机从序列元素中取出一个值&#xff0c;…

Hazelcast分布式内存网格(IMDG)基本使用,使用Hazelcast做分布式内存缓存

文章目录 一、Hazelcast简介1、Hazelcast概述2、Hazelcast之IMDG3、数据分区 二、Hazelcast配置1、maven坐标2、集群搭建&#xff08;1&#xff09;组播自动搭建 3、客户端4、集群分组5、其他配置 三、Hazelcast分布式数据结构1、IMap2、IQueue&#xff1a;队列3、MultiMap4、I…

LINUX:如何以树形结构显示文件目录结构

tree tree命令用于以树状图列出目录的内容。 第一步&#xff0c;先安装tree这个包 sudo apt-get install tree 第二步&#xff0c;在指定文件目录输入下面命令&#xff0c;7代表7级子目录 tree -L 7 第三步&#xff0c;效果图 第四步&#xff0c;拓展学习 颜色显示 tree -C显…

mysql中除了InnoDB以外的其它存储引擎

参考资料&#xff1a;https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html MyISAM存储引擎 https://dev.mysql.com/doc/refman/8.0/en/myisam-storage-engine.html MyISAM 存储引擎是基于比较老的ISAM存储引擎&#xff08;ISAM已经不再可用&#xff09;&#xff…

09、pytest多种调用方式

官方用例 # content of myivoke.py import sys import pytestclass MyPlugin:def pytest_sessionfinish(self):print("*** test run reporting finishing")if __name__ "__main__":sys.exit(pytest.main(["-qq"],plugins[MyPlugin()]))# conte…