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

news2024/11/29 23:46:51

目录

一、单机限流

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()
         return
      case <-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 int64
   vistCount    int64
   interval     int64
   maxCount     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.List
   interval   int64
   maxCnt     int
   lock       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  string
   interVal int
   limitCnt int
   redis    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 then
    if limitCnt<1 then
        return "true"
    else
        redis.call('set',key,1,'PX',ARGV[1])
        return "false"
    end
elseif tonumber(val)<limitCnt then
    redis.call('incr',key)
    return "false"
else
    return "true"
end
2、分布式滑动窗口
//go:embed lua/slidewindow.lua

var slideWindLua string
​
type redisSlib struct {
   serverName string
   interVal   time.Duration
   maxCnt     int64
   redis      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 then
    return "true"
else
    redis.call('ZADD',key,now,now)
    redis.call('PEXPIRE',key,window)
    return "false"
end

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

php操作数据库,用wampserver工具

php操作数据库&#xff0c;用wampserver工具 打开wampserver数据库可视化&#xff0c;创建表格&#xff0c;插入数据 DROP TABLE IF EXISTS user; CREATE TABLE IF NOT EXISTS user (user_Id int NOT NULL AUTO_INCREMENT COMMENT 用户编号,user_Name varchar(20) CHARACTER S…

MySQL生成UUID并去除-

uuid()函数 uuid() 函数可以使mysql生成uuid,但是uuid中存在-,如下图&#xff1a; 去除uuid的- 默认生成的uuid含有-&#xff0c;我们可以使用replace函数替换掉-&#xff0c;SQL如下 select replace(uuid(),"-","") as uuid;Insert语句中使用UUID 如果…

VR远程带看,助力线下门店线上化转型“自救”

VR远程带看&#xff0c;因自身高效的沉浸式在线沟通功能&#xff0c;逐渐走进了大众的视野。身临其境的线上漫游体验以及实时同屏互联的新型交互模式&#xff0c;提升了商家同用户之间的沟通效率&#xff0c;进一步实现了远程线上一对一、一对多的同屏带看&#xff0c;用户足不…

解决Error:You‘re using an RSA key with SHA-1, which is no longer allowed

一、问题 在微信开发者工具中&#xff0c;推送代码时发生错误Error:You‘re using an RSA key with SHA-1, which is no longer allowed...... 奇怪的是命令行可以正常push: 原因&#xff1a;因为生成密钥的RSA算法&#xff0c;由于安全性原因&#xff0c;现在已经不允许使用…

Elasticsearch:什么是机器学习?

机器学习定义 机器学习 (ML) 是人工智能 (AI) 的一个分支&#xff0c;专注于使用数据和算法来模仿人类的学习方式&#xff0c;并随着时间的推移逐渐提高准确性。 计算机科学家和人工智能创新者 Arthur Samuel 在 20 世纪 50 年代首次将其定义为 “赋予计算机无需明确编程即可学…

【每日一题】—— C. Removal of Unattractive Pairs(Codeforces Round 913 (Div. 3))(思维)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

【FPGA图像处理实战】- 图像处理前景如何?就业前景如何?

图像处理是FPGA应用的主要领域之一&#xff0c;图像处理数据量特别大且对实时性处理要求高的场景&#xff0c;这恰好能发挥FPGA流水线可实时处理的优势。 那么FPGA图像处理的前景如何&#xff1f; 一、FPGA开发&#xff08;图像处理&#xff09;招聘就业情况 看FPGA图像处理…

Notes数据直接在Excel中统计

大家好&#xff0c;才是真的好。 我希望你看过前面两篇内容《Domino REST API安装和运行》和《Domino REST API安装和运行》&#xff0c;因为今天我们正是使用REST API方式在Excel中查询和统计Notes数据。 不过首先你得知道一个OData协议&#xff0c;全名Open Data Protocol(…

智能优化算法应用:基于蜜獾算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蜜獾算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蜜獾算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蜜獾算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

Unity中Batching优化的静态合批

文章目录 前言一、静态合批的规则1、模型使用同一个材质2、勾选静态合批3、对于静态合批后的Mesh顶点总数&#xff0c;不超过2^16^即可以使用同一批次&#xff0c;超过则会开启一个新的批次4、对与使用同一材质的不同模型间&#xff0c;纹理贴图的问题&#xff0c;我们可以通过…

基于高德API实现网络geoJSON功能(突出省份)

代码实现&#xff1a; <script>// 3、初始化一个高德图层const gaode new ol.layer.Tile({title: "高德地图",source: new ol.source.XYZ({url: http://wprd0{1-4}.is.autonavi.com/appmaptile?langzh_cn&size1&style7&x{x}&y{y}&z{z},w…

git 面试字节时,老师问:合并分支中 rebase 和 merge 的区别

实际开发工作的时候&#xff0c;我们都是在自己的分支开发&#xff0c;然后将自己的分合并到主分支&#xff0c;那合并分支用2种操作&#xff0c;这2种操作有什么区别呢&#xff1f; git上新建一个项目&#xff0c;默认是有master分支的&#xff0c;将项目克隆到本地&#xff…

C/C++,数值计算——快速幂(Quick Pow)的几种算法源代码

1 文本格式 // C Version long long binpow(long long a, long long b) { if (b 0) return 1; long long res binpow(a, b / 2); if (b % 2) return res * res * a; else return res * res; } // C Version long long binpow(long long a,…

Nginx的安装、升级和管理

目录 一. nginx介绍 1. nginx简介 2. nginx和apache区别 二. nginx编译安装 1. 下载解压nginx安装包&#xff0c;并安装nginx依赖包 2. 创建运行用户和组 3. 编译安装并补全 4. 效验结果 三. 平滑升级nginx 1. 下载解压nginx安装包 2. 编译安装 3. 替换二进制文件 …

uniapp使用v-html调用接口,富文本图片 视频自适应大小

前端获取到后台数据 不做处理 就会出现下面问题 图片 视频超出视图显示不全 处理 //info 是富文本 <view v-ifinfo v-htmlreplaceWhite(info)></view>调用下面方法 replaceWhite(html) { // 处理富文本默认图片&#xff0c;视频大小let newContent html.replace…

EPICS modbus 模块数字量读写练习

本文使用modbus slave软件模拟一个受控的modbus设备&#xff0c;此模拟设备提供如下功能&#xff1a; 1、线圈1&#xff0c;起始地址为0&#xff0c;数量为8&#xff0c;软件设置如下(功能码1)&#xff1a; 2、线圈2&#xff0c;起始地址为8&#xff0c;数量为8&#xff0c;软…

easyui实现省市县三级联动

一、技术: 前端采用的是easyui+jquery+jsp页面 后端采用springmvc+mybatis+mysql8 效果图 二、cascadeEasyui.jsp页面 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%String path = request.getContex…

Stable Diffusion 系列教程 - 2 WebUI 参数详解

Stable Diffusion 的整个算法组合为&#xff1a; UNet VAE 文本编码器 UNet&#xff1a;就是我们大模型里的核心。 文本编码器&#xff1a;将我们的prompt进行encoder为算法能理解的内容&#xff08;可以理解为SD外包出去的项目CLIP&#xff09;。 VAE&#xff1a;对UNet生…

现货白银简单介绍

在贵金属投资领域&#xff0c;现货白银是当前国际上最为流行、交投最为活跃的白银投资方式&#xff0c;其交易市场遍布全球&#xff0c;包括伦敦、苏黎世、纽约、芝加哥及香港等主要市场&#xff0c;是一种以杠杆交易和做市商的形式进行的现货交易。 现货白银可以说是当下交易模…

ELK的日志解决方案

ELK的日志解决方案 ELK是什么 ELK 是一个缩写&#xff0c;代表 Elastic Stack&#xff0c;而不是三个独立的产品名称。Elastic Stack 是一个开源的数据处理和分析平台&#xff0c;用于实时搜索、分析和可视化大规模数据。ELK 是由三个主要的组件构成&#xff1a; Elasticsea…