在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流

缓存:缓存的目的是提升系统访问速度和增大系统处理容量

降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行

限流:限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理

漏桶算法

漏斗有一个进水口 和 一个出水口,出水口以一定速率出水,并且有一个最大出水速率:

在漏斗中没有水的时候,

如果进水速率小于等于最大出水速率,那么,出水速率等于进水速率,此时,不会积水

如果进水速率大于最大出水速率,那么,漏斗以最大速率出水,此时,多余的水会积在漏斗中

在漏斗中有水的时候,出水口以最大速率出水

如果漏斗未满,且有进水的话,那么这些水会积在漏斗中

如果漏斗已满,且有进水的话,那么这些水会溢出到漏斗之外

令牌桶算法

对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。

令牌桶算法的原理是系统以恒定的速率产生令牌,然后把令牌放到令牌桶中,令牌桶有一个容量,当令牌桶满了的时候,再向其中放令牌,那么多余的令牌会被丢弃;当想要处理一个请求的时候,需要从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝该请求。

ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(100));

// 指定每秒放1个令牌

RateLimiter limiter = RateLimiter.create(1);

for (int i = 1; i < 50; i++) {

// 请求RateLimiter, 超过permits会被阻塞

//acquire(int permits)函数主要用于获取permits个令牌,并计算需要等待多长时间,进而挂起等待,并将该值返回

Double acquire = null;

if (i == 1) {

acquire = limiter.acquire(1);

} else if (i == 2) {

acquire = limiter.acquire(10);

} else if (i == 3) {

acquire = limiter.acquire(2);

} else if (i == 4) {

acquire = limiter.acquire(20);

} else {

acquire = limiter.acquire(2);

}

executorService.submit(new Task("获取令牌成功,获取耗:" + acquire + " 第 " + i + " 个任务执行"));

}

...

static class Task implements Runnable {

String str;

public Task(String str) {

this.str = str;

}

@Override

public void run() {

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

System.out.println(sdf.format(new Date()) + " | " + Thread.currentThread().getName() + str);

}

}

2019-07-06 16:48:32.495 | pool-1-thread-1获取令牌成功,获取耗:0.0 第 1 个任务执行

2019-07-06 16:48:33.437 | pool-1-thread-2获取令牌成功,获取耗:0.98403 第 2 个任务执行

2019-07-06 16:48:43.434 | pool-1-thread-3获取令牌成功,获取耗:9.993843 第 3 个任务执行

2019-07-06 16:48:45.434 | pool-1-thread-4获取令牌成功,获取耗:1.996439 第 4 个任务执行

2019-07-06 16:49:05.436 | pool-1-thread-5获取令牌成功,获取耗:19.997107 第 5 个任务执行

2019-07-06 16:49:07.434 | pool-1-thread-6获取令牌成功,获取耗:1.995649 第 6 个任务执行

2019-07-06 16:49:09.432 | pool-1-thread-7获取令牌成功,获取耗:1.997342 第 7 个任务执行

2019-07-06 16:49:11.432 | pool-1-thread-8获取令牌成功,获取耗:1.999978 第 8 个任务执行

2019-07-06 16:49:13.433 | pool-1-thread-9获取令牌成功,获取耗:1.999568 第 9 个任务执行

2019-07-06 16:49:15.439 | pool-1-thread-10获取令牌成功,获取耗:1.999212 第 10 个任务执行

2019-07-06 16:49:17.435 | pool-1-thread-11获取令牌成功,获取耗:1.993059 第 11 个任务执行

2019-07-06 16:49:19.434 | pool-1-thread-12获取令牌成功,获取耗:1.99708 第 12 个任务执行

分布式限流Redis+Lua

local key = "rate.limit:" .. KEYS[1] -- 限流KEY

local limit = tonumber(ARGV[1]) -- 限流大小

local current = tonumber(redis.call('get', key) or "0") + 1

if current > limit then -- 如果超出限流大小

return 0

else -- 请求数+1,并设置2秒过期

redis.call("INCRBY", key, "1")

redis.call("EXPIRE", key, "2")

return current

end

-- https://github.com/junhc/rate-limiter

-- 判断source_str 中是否contains pattern_str

-- @param source_str

-- @param patter_str

local function contains(source_str, sub_str)

local start_pos, end_pos = string.find(source_str, sub_str);

if start_pos == nil then

return false;

end

local source_str_len = string.len(source_str);

if source_str_len == end_pos then

return true

elseif string.sub(source_str, end_pos + 1, end_pos + 1) == "," then

return true

end

return false;

end

-- 获取令牌

-- 返回码

-- 0 没有令牌桶配置

-- -1 表示取令牌失败,也就是桶里没有令牌

-- 1 表示取令牌成功

-- @param key 令牌的唯一标识

-- @param permits 请求令牌数量

-- @param curr_mill_second 当前毫秒数

-- @param context 使用令牌的应用标识

local function acquire(key, permits, curr_mill_second, context)

local rate_limit_info = redis.pcall("HMGET", key, "last_mill_second", "curr_permits", "max_permits", "rate", "apps")

local last_mill_second = rate_limit_info[1]

local curr_permits = tonumber(rate_limit_info[2])

local max_permits = tonumber(rate_limit_info[3])

local rate = rate_limit_info[4]

local apps = rate_limit_info[5]

-- 标识没有配置令牌桶

if type(apps) == 'boolean' or apps == nil or not contains(apps, context) then

return 0

end

local local_curr_permits = max_permits;

-- 令牌桶刚刚创建,上一次获取令牌的毫秒数为空

-- 根据和上一次向桶里添加令牌的时间和当前时间差,触发式往桶里添加令牌,并且更新上一次向桶里添加令牌的时间

-- 如果向桶里添加的令牌数不足一个,则不更新上一次向桶里添加令牌的时间

if (type(last_mill_second) ~= 'boolean' and last_mill_second ~= nil) then

local reverse_permits = math.floor(((curr_mill_second - last_mill_second) / 1000) * rate)

local expect_curr_permits = reverse_permits + curr_permits;

local_curr_permits = math.min(expect_curr_permits, max_permits);

-- 大于0表示不是第一次获取令牌,也没有向桶里添加令牌

if (reverse_permits > 0) then

redis.pcall("HSET", key, "last_mill_second", curr_mill_second)

end

else

redis.pcall("HSET", key, "last_mill_second", curr_mill_second)

end

local result = -1

if (local_curr_permits - permits >= 0) then

result = 1

redis.pcall("HSET", key, "curr_permits", local_curr_permits - permits)

else

redis.pcall("HSET", key, "curr_permits", local_curr_permits)

end

return result

end

-- 初始化令牌桶配置

-- @param key 令牌的唯一标识

-- @param max_permits 桶大小

-- @param rate 向桶里添加令牌的速率

-- @param apps 可以使用令牌桶的应用列表,应用之前用逗号分隔

local function init(key, max_permits, rate, apps)

local rate_limit_info = redis.pcall("HMGET", key, "last_mill_second", "curr_permits", "max_permits", "rate", "apps")

local org_max_permits = tonumber(rate_limit_info[3])

local org_rate = rate_limit_info[4]

local org_apps = rate_limit_info[5]

if (org_max_permits == nil) or (apps ~= org_apps or rate ~= org_rate or max_permits ~= org_max_permits) then

redis.pcall("HMSET", key, "max_permits", max_permits, "rate", rate, "curr_permits", max_permits, "apps", apps)

end

return 1;

end

-- 删除令牌桶

local function delete(key)

redis.pcall("DEL", key)

return 1;

end

local key = KEYS[1]

local method = ARGV[1]

if method == 'acquire' then

return acquire(key, ARGV[2], ARGV[3], ARGV[4])

elseif method == 'init' then

return init(key, ARGV[2], ARGV[3], ARGV[4])

elseif method == 'delete' then

return delete(key)

else

-- ignore

end

参考资料

令牌桶算法和漏桶算法python_限流之漏桶算法与令牌桶算法相关推荐

  1. 什么是限流?为什么会限流呢?常见的限流算法【固定窗口限流、滑动窗口限流、漏桶限流、令牌桶限流】是什么呢?

    什么是限流?为什么会限流呢?常见的限流算法[固定窗口限流.滑动窗口限流.漏桶限流.令牌桶限流]是什么呢? 什么是限流? 为什么会限流? 1. 固定窗口限流算法 1.1 什么是固定窗口限流算法 1.2 ...

  2. 可能要用心学高并发核心编程,限流原理与实战,分布式令牌桶限流

    实战:分布式令牌桶限流 本节介绍的分布式令牌桶限流通过Lua+Java结合完成,首先在Lua脚本中完成限流的计算,然后在Java代码中进行组织和调用. 分布式令牌桶限流Lua脚本 分布式令牌桶限流Lu ...

  3. java redis 限流_Redis——限流算法之滑动窗口、漏斗限流的原理及java实现

    限流的意义 限流一般是指在一个时间窗口内对某些操作请求的数量进行限制,比如一个论坛限制用户每秒钟只能发一个帖子,每秒钟只能回复5个帖子.限流可以保证系统的稳定,限制恶意请求,防止因为流量暴增导致系统瘫 ...

  4. 高并发策略之限流:计数器、漏桶、令牌桶 三大算法的原理与实战(史上最全)

    导读 网站高可用指的就是:在绝大多的时间里,网站一直处于可以对外提供服务的正常状态. 一般以"年"为单位来统计,"9"的个数越多,代表一年中允许的不可用时间就越 ...

  5. 简单介绍4种限流算法!(固定窗口计数器算法、滑动窗口计数器算法、漏桶算法、令牌桶算法)...

    作者:架构小菜 链接:https://www.jianshu.com/p/7987bf427b5b 简单介绍 4 种非常好理解并且容易实现的限流算法! 一.固定窗口计数器算法 规定我们单位时间处理的请 ...

  6. 限流算法之漏桶算法、令牌桶算法

    限流 每个API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性.即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统 ...

  7. 高并发系统限流-漏桶算法和令牌桶算法

    参考: https://www.cnblogs.com/xuwc/p/9123078.html http://www.cnblogs.com/LBSer/p/4083131.html https:// ...

  8. 【面试大全-高并发】-限流策略有哪些,滑动窗口算法和令牌桶区别,使用场景

    参考思路:限流算法常用的几种实现方式有如下四种:计数器.滑动窗口.漏桶和令牌桶: ● 计数器: ○ 思想:在固定时间窗口内对请求进行计数,与阀值进行比较判断是否需要限流,一旦到了时间临界点,将计数器清 ...

  9. 分析常见限流算法及手写三种(计数器、漏斗、令牌桶)代码实现

    常见的限流算法分析 限流在我们日常生活中经常见到,如火车站门口的栏杆.一些景点的门票只出售一定的数量 等等.在我们的开发中也用到了这种思想. 为什么要限流 在保证可用的情况下尽可能多增加进入的人数,其 ...

最新文章

  1. 这套“人造肌腱”装备,可保护你的老腰|Science子刊
  2. 集群scan_扫描k8s集群中的漏洞
  3. Facebook全面推出Watch Party,可多人线上同看直播视频
  4. 用训练好的paddlepaddle模型继续训练模型和验证数据ckpt
  5. 梯度为什么是函数增加最快的方向
  6. python set 和 ^ 的妙用
  7. PDF文档解析,公司公告信息抽取(附数据集)
  8. Tkinter 的 Text 组件
  9. 从无线安全到内网渗透[1]
  10. 记录实验所用计算机硬件配置,《计算机硬件性能检测》实验指导书
  11. uniapp 学习笔记三十 结算页面结构搭建地址列表页面结构搭建
  12. RF射频传输,原理介绍,三分钟看懂!发射功率、接收灵敏度详解!
  13. 【基于Centos】驱动安装
  14. python表示倍数_倍数 python
  15. win10 wmware 花屏_用了N年浑不知!原来Win10竟有这么多隐藏功能
  16. Highcharts使用小心得
  17. android设备打开5555远程连接端口
  18. 【Python】CPython解释器及字节码
  19. uc浏览器网盘无限收藏_你很有可能不知道谷歌浏览器上面隐藏着这个小秘密!...
  20. 林大ACM培训心得day4

热门文章

  1. Windows 游戏之父,DirectX 作者 Eric Engstrom 意外去世,享年55岁
  2. 20 周年特别策划 | 说出你与 CSDN 的故事!
  3. AI ProCon 2020 圆满落幕,百位专家与万名开发者共同拉开人工智能新篇章
  4. 倒计时2天 | AI开发者大会完整议程揭秘!与百名大咖玩转人工智能
  5. @程序员,为你揭开直播技术的神秘面纱!
  6. Kotlin 风险高、RxJava 已过时,Android 原生开发现状分析!
  7. 程序员的修神之路是?
  8. 搭乘云原生与数据中台实践列车 通往数字化转型前沿之旅
  9. 看完就能用!这本Python3.6 的书玩大了!网友:牛!
  10. 揭秘计算机之间互发数据的关键原理!