细品服务并发限流+Redis-cell的使用

2023-10-21 01:10

本文主要是介绍细品服务并发限流+Redis-cell的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

今天热搜“海底捞的排号系统挂掉了”,也许是今天情人节,各位情侣去海底捞约会,进入排号系统的流量猛增,导致服务支撑不住,直接挂掉,在这里只是猜测(大胆猜测,小心求证)。那我们应该如何防止因为流量突然猛增而导致服务挂掉的问题呢?那就是限流了。
那我们通过redis 来设计限流策略。

服务限流

简介

  • 通过压测我们可以压出我们服务接口可以承受最大的QPS或者TPS,但是我们压测的话只是单压并不知道在生产环境所能承受的最大流量。如果说其他业务接口也在跑,那这就很难把控这个接口在生产环境可以定多大的QPS或TPS。所以预估某个接口的所能承受的QPS和TPS还是很有水平的。我能力有限今天只聊如何限流。

时间窗口限流

什么是时间窗口?
  • TCP/IP为了提高传输效率(提高吞吐量)采用并发进行传输包,由于有ACk机制,如果等并发发出去的包都回来的话,会影响整体的发送效率,所以只要等到他需要等待的数据就进行发起第二次的传输。有人就会问了,TCP包的传输是有序的,如果并发发送的包,顺序是在后面的包先回来了,那怎么搞,那就继续等待先去的包回来再进行下次操作。
    还有就是采用了并发那还得考虑机器的性能,可不能由于发送的包太多导致发送包的服务不可用了。于是就有了滑动时间窗口协议。
  • 具体TCP/IP滑动时间窗口协议详解
  • 滑动时间窗口主要解决的问题就是控制瞬时的量,通过缓存将其分为4个区段,进行滑动处理这4个区段。当这四个区段放满后就会进行等待。想了解得更有深度,可以点击上面链接进行深度学习。下图是TCP滑动窗口示意图
    在这里插入图片描述

时间滑动窗口协议的应用

  1. 现在我们的服务使用的是java语言,现在需要实现一个滑动窗口。
    2.使用ReentrantLock(可重入锁)实现,如下图 这样有个问题就是:粒度太大了,不均匀,针对1秒一下的,没法辨析。
    我们能不能把粒度拆细了,1秒拆成10个100毫秒。每一个100毫秒有一个计数器。了解TCP/IP的应该知道,TCP/IP为了增加传输速度和控制传输速度,有个叫“滑动窗口协议”。就算拆得再细,也无法解决匀速限制速度的问题。而且还有个临界点问题,比如假如,一秒限制10个请求,在第1秒钟,第2秒 之间,第1秒后半段时间10个请求,第2秒前半段10个请求,那第1秒后半段+第2秒前半段时间组成的一秒钟里就有20个请求,没有起到限速的作用。
    在这里插入图片描述

  2. java中的JUC包中的CyclicBarrier。实现一个限流。我们只允许最多执行多少个线程。如果其中一个阻塞了。那这就很尴尬了。会影像服务的正常的吞吐,但是上面的那种方式,当他阻塞了后,随着时间窗口的推进,会将上一次时间串口的请求的技术进行归零。

漏斗限流

漏斗限流是最常用的限流方法之一,顾名思义,这个算法的灵感源于漏斗(funnel)的结
构。
在这里插入图片描述

实现一个简单的漏斗算法
package 漏斗算法;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class FunnelRateLimiter {static class Funnel {//漏斗的容量int capacity;//速率float leakingRate;//剩余容量int leftQuota;long leakingTs;public Funnel(int capacity, float leakingRate) {this.capacity = capacity;this.leakingRate = leakingRate;this.leftQuota = capacity;this.leakingTs = System.currentTimeMillis();}void makeSpace() {long nowTs = System.currentTimeMillis();long deltaTs = nowTs - leakingTs;int deltaQuota = (int) (deltaTs * leakingRate);// 间隔时间太长,整数数字过大溢出if (deltaQuota < 0) {this.leftQuota = capacity;this.leakingTs = nowTs;return;}// 腾出空间太小,最小单位是 1if (deltaQuota < 1) {return;}this.leftQuota += deltaQuota;this.leakingTs = nowTs;if (this.leftQuota > this.capacity) {this.leftQuota = this.capacity;}}boolean watering(int quota) {makeSpace();if (this.leftQuota >= quota) {this.leftQuota -= quota;return true;}return false;}}private Map<String, Funnel> funnels = new ConcurrentHashMap<>();public boolean isActionAllowed(String userId, String actionKey, int capacity, float leakingRate) {String key = String.format("%s:%s", userId, actionKey);Funnel funnel = funnels.get(key);if (funnel == null) {funnel = new Funnel(capacity, leakingRate);funnels.put(key, funnel);}// 需要 1 个 quotareturn funnel.watering(1);}
}
  • 有人就会怀疑自己写的不靠谱,写在服务里面使用内存这更不靠谱,有没有可以使用的中间件,被人造好的轮子。还真有,redis-cell
Redis-cell 的使用
  • Redis 4.0 提供了一个限流 Redis 模块,它叫 redis-cell。该模块也使用了漏斗算法,并
    提供了原子的限流指令。有了这个模块,限流问题就非常简单了。
  • 该模块只有 1 条指令 cl.throttle,它的参数和返回值都略显复杂,接下来让我们来看看这
    个指令具体该如何使用
    在这里插入图片描述

上面这个指令的意思是允许「用户laoqian回复行为」的频率为每 60s 最多 30 次(漏水速
率),漏斗的初始容量为 15,也就是说一开始可以连续回复 15 个帖子,然后才开始受漏水
速率的影响。我们看到这个指令中漏水速率变成了 2 个参数,替代了之前的单个浮点数。用
两个参数相除的结果来表达漏水速率相对单个浮点数要更加直观一些。

> cl.throttle laoqian:reply 15 30 60
1) (integer) 0 # 0 表示允许,1 表示拒绝
2) (integer) 15 # 漏斗容量 capacity
3) (integer) 14 # 漏斗剩余空间 left_quota
4) (integer) -1 # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
5) (integer) 2 # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒)
  • 在执行限流指令时,如果被拒绝了,就需要丢弃或重试。cl.throttle 指令考虑的非常周
    到,连重试时间都帮你算好了,直接取返回结果数组的第四个值进行 sleep 即可,如果不想
    阻塞线程,也可以异步定时任务来重试(放入一个队列进行消费队列)。

总结

  • 两种时间限流方法 时间窗口限流和漏斗限流
  • 简单介绍了时间窗口限流在TCP中的应用,TCP为了达到并发,且安全可靠传输采用时间窗口协议进行并发可靠传输包
  • 时间窗口的限流方式不能达到顺滑,为达到顺滑限流采用漏都限流。使用java简单实现漏斗限流
  • Redis4.0 中cell的使用。完美的且简单的就可以实现限流。

参考

  • 《redis 深度历险》
  • https://juejin.im/entry/6844903695432286215
  • https://www.jianshu.com/p/41781605ed29
  • 其他限流算法(令牌桶https://www.google.com/search?q=%E4%BB%A4%E7%89%8C%E6%A1%B6&oq=%E4%BB%A4%E7%89%8C%E6%A1%B6&aqs=chrome…69i57j69i59j0l4.1915j0j7&sourceid=chrome&ie=UTF-8)

这篇关于细品服务并发限流+Redis-cell的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/250923

相关文章

Mysql中RelayLog中继日志的使用

《Mysql中RelayLog中继日志的使用》MySQLRelayLog中继日志是主从复制架构中的核心组件,负责将从主库获取的Binlog事件暂存并应用到从库,本文就来详细的介绍一下RelayLog中... 目录一、什么是 Relay Log(中继日志)二、Relay Log 的工作流程三、Relay Lo

使用Redis实现会话管理的示例代码

《使用Redis实现会话管理的示例代码》文章介绍了如何使用Redis实现会话管理,包括会话的创建、读取、更新和删除操作,通过设置会话超时时间并重置,可以确保会话在用户持续活动期间不会过期,此外,展示了... 目录1. 会话管理的基本概念2. 使用Redis实现会话管理2.1 引入依赖2.2 会话管理基本操作

Springboot请求和响应相关注解及使用场景分析

《Springboot请求和响应相关注解及使用场景分析》本文介绍了SpringBoot中用于处理HTTP请求和构建HTTP响应的常用注解,包括@RequestMapping、@RequestParam... 目录1. 请求处理注解@RequestMapping@GetMapping, @PostMappin

springboot3.x使用@NacosValue无法获取配置信息的解决过程

《springboot3.x使用@NacosValue无法获取配置信息的解决过程》在SpringBoot3.x中升级Nacos依赖后,使用@NacosValue无法动态获取配置,通过引入SpringC... 目录一、python问题描述二、解决方案总结一、问题描述springboot从2android.x

SpringBoot整合AOP及使用案例实战

《SpringBoot整合AOP及使用案例实战》本文详细介绍了SpringAOP中的切入点表达式,重点讲解了execution表达式的语法和用法,通过案例实战,展示了AOP的基本使用、结合自定义注解以... 目录一、 引入依赖二、切入点表达式详解三、案例实战1. AOP基本使用2. AOP结合自定义注解3.

Python中Request的安装以及简单的使用方法图文教程

《Python中Request的安装以及简单的使用方法图文教程》python里的request库经常被用于进行网络爬虫,想要学习网络爬虫的同学必须得安装request这个第三方库,:本文主要介绍P... 目录1.Requests 安装cmd 窗口安装为pycharm安装在pycharm设置中为项目安装req

使用Python将PDF表格自动提取并写入Word文档表格

《使用Python将PDF表格自动提取并写入Word文档表格》在实际办公与数据处理场景中,PDF文件里的表格往往无法直接复制到Word中,本文将介绍如何使用Python从PDF文件中提取表格数据,并将... 目录引言1. 加载 PDF 文件并准备 Word 文档2. 提取 PDF 表格并创建 Word 表格

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA

Python使用Matplotlib和Seaborn绘制常用图表的技巧

《Python使用Matplotlib和Seaborn绘制常用图表的技巧》Python作为数据科学领域的明星语言,拥有强大且丰富的可视化库,其中最著名的莫过于Matplotlib和Seaborn,本篇... 目录1. 引言:数据可视化的力量2. 前置知识与环境准备2.1. 必备知识2.2. 安装所需库2.3

Python数据验证神器Pydantic库的使用和实践中的避坑指南

《Python数据验证神器Pydantic库的使用和实践中的避坑指南》Pydantic是一个用于数据验证和设置的库,可以显著简化API接口开发,文章通过一个实际案例,展示了Pydantic如何在生产环... 目录1️⃣ 崩溃时刻:当你的API接口又双叒崩了!2️⃣ 神兵天降:3行代码解决验证难题3️⃣ 深度