首页 > 其他分享 >限流器的实践

限流器的实践

时间:2024-09-19 16:01:45浏览次数:9  
标签:令牌 请求 实践 接口 学校 限流 客户端

背景

我们有一个业务场景是给学生发布考试,发布的过程不复杂,就是一个老师传递一些考试相关的参数过来,服务器自动给所有学生生成一份任务,但是在学生上交的时候会有个问题,就是成百上千的学生一起上交,会有并发流量的问题。

这里由于我们的考试可能会设计多个班级的联考,乃至一个学校或多个学校的联考,因为上交成绩单是一个比较集中的时间段,因此这里需要考虑的是服务器能承受的最大QPS以及学校的流量峰值。

这里比较特殊的是需要考虑学校的流量峰值,我们不希望因为考试把学校网络给整瘫痪了。

功能实现

鉴于上面两种情况,我首先想到的是限流,因此就有了第一种方案

方案一

采用阿里的Sentinel或者Guava的RateLimiter限流器,限流器的好处在于能对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。

这里不去研究限流采用的是滑动窗口法,还是漏桶算法或是令牌桶算法,因为这些都能达到我们的目的,而且框架已经封装的很好。

限流的大小如何去确定呢,我们可以根据实际情况计算得出,int maxQps=学校最大允许上传流量*某个百分比/每份作业大小(我们每份作业按50KB算),这里的百分比是控制流量的大小,可以设定为80%或90%。

客户端请求时,超过maxQps的请求就return false,然后客户端做延时重试上传。

下面是流程图:

现在看上去已经很完美了,把限流器一加,就已经达到了控制QPS的目的,但其实这里有个很大的问题,服务器的QPS是控制住了,但是学校的流量并没有控制住,学校每次都是把所有作业数据请求过来,然后才知道这次请求是true还是false的,这样还是会导致学校流量瘫痪。

因此就有了下面的第二种方案

方案二

在HTTP跨域请求中,在正式跨域之前,浏览器会根据需要发起一次预检(也就是option请求,这次请求是不带任何数据的),用来让服务端返回允许的方法(如get、post),被跨域访问的Origin(来源或者域),还有是否需要Credentials(认证信息)等。当然跨域的PreFlight也有个前提,就是用了自定义的请求头,不过这些都不是重点,这里我引用这个option请求知识点的目的在于,我们是否也可以运用这种思想。

第一步,客户端先发送一次类似option的PreFlight请求去获取token,这个token就是是否能进行第二次请求的令牌。

第二步,服务器接受到请求,根据限流器返回结果(true或false)。

第三步,客户端根据返回的结果,来决定是否进行真实的请求,如果是false则需要重试。

第四步,假设客户端已经获取到令牌,然后发送真实的请求。

流程图:

通过上面的操作,我们可以一直限制请求个数在maxQps范围内,因为永远最多只有maxQps的令牌可以通过申请,也就只有maxQps的客户端可以发送出真实请求,达到了控制学校流量的目的。

这样看上去依旧很完美,然而很不幸,这个方案还是有很大的问题:

假定我们设置的令牌桶(或者滑动窗口)大小为5,则必然会出现真实请求和PreFlight请求同时出现在桶里的情况,结果可能是一个桶里面的请求大部分都是这样PreFlight请求,就会导致真实请求数量达不到预期的结果,使QPS大大下降。

而且如果PreFlight请求和真实请求是同一个接口也不利于接口参数的判断。

造成这个结果的原因是因为真实请求和PreFlight请求同时出现在一个桶里,那我们把他们分开,于是就有了方案三。

方案三

在这里,我把PreFlight请求单独设立一个getToken接口,和上交考试的接口分开,然后PreFlight请求设立一个限流器,称作A限流器。

第一步,客户端调用getToken接口,获取令牌。

第二步,服务器根据限流器返回结果(true或false)。

第三步,客户端根据返回的结果,来决定是否进行真实的请求,如果是false则需要重试。

第四步,假设客户端已经获取到令牌,然后发送真实的请求。

这里的操作步骤和上面是一样的,无非就是把Token请求接口给单独分离,这样不会影响真实请求的发送,并且实现了限制客户端每秒能获取到的令牌数量,也就实现了限制真实请求的QPS。

(2)获取Token接口由于限流是针对一个学校的流量的,所以这里还得考虑多个学校联考时,各个学校Token的获取不能受干扰,这样就得给每个学校设置一个限流器。

我查阅了阿里的Sentinel根据接口限流的功能,Sentinel根据可以实现根据不同接口限流,但是需要给固定的接口设置限流规则,也就是这接口一开始就定义好的,如果是接口里有路径参数(如/v1/homework/token/{schoolId}),就会视为不同接口,也就需要配置不同的规则。所以这种方案是不符合我们的场景的,所以我自己摸索了一套方法用于给每个学校设定限流器。

大体思路就是每次调用Token请求时,我都会创建一个这个学校的规则,并且和已经存在的规则去做对比,如果已存在规则里有这个学校的规则,就不添加,不然就添加到规则里,并重载到FlowRuleManager。代码如下:

/**
* 限流器规则 
*/
private Set<FlowRule> rules = new ConcurrentSkipListSet<>((rule1, rule2) -> Objects.equals(rule1, rule2) ? 0 : 1);
/**
* 获取是否能上交考试作业的令牌 
* 
* @return 
*/
public Boolean getToken(Integer schoolId) {
String resourceName = ConstantUtil.Sentinel.EXAM_RULE_PREFIX + schoolId;
FlowRule rule = new FlowRule(resourceName);
rule.setCount(100);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//每次进来都创建规则,如果这个学校没在限流列表,那就加入规则列表,并重载规则
  if (rules.add(rule)) {
    FlowRuleManager.loadRules(Lists.newArrayList(rules));
  }    
 return SphO.entry(resourceName);
}

这里限流器规则rules需要用Set存储,防止重复(因为Sentinel规则是List的,不能去重,所以需要我们自己去重),Set我选择了ConcurrentSkipListSet来存储,效率较高,而且是线程安全的,不会有并发问题。

(3)接下来为了不影响QPS性能,我们在上交考试模块增加了一个RabbitMQ,能增加QPS的同时,也能减少数据库的压力,所以最后的逻辑图是这样的:

这里稍微提一下,RabbitMQ我会增加一个死信队列,用于保存推送失败时数据的保存,死信队列的数据我会保存到dead_exam_data表,当线上出现问题时,可以通过手动调用/v1/dead/exam接口,把死信数据提取出来,重新保存到HomeworkDetail作业表。

标签:令牌,请求,实践,接口,学校,限流,客户端
From: https://www.cnblogs.com/leecoder5/p/18420799

相关文章

  • 机器学习-贝叶斯算法的研究和实践(评论检测)
    项目背景:★我们公司的应用有个需求是对用户发表的评论进行过滤,除了人工审核干预以外,我们还需要自动化检测评论来规避这些行为,为此我们研究贝叶斯算法,写了评论检测的项目用于过滤垃圾评论。贝叶斯算法介绍​贝叶斯分类算法是统计学的一种分类方法,它是一类利用概率统计知识......
  • 京东短网址高可用提升最佳实践
    什么是短网址?短网址,是在长度上比较短的网址。简单来说就是帮您把冗长的URL地址缩短成8个字符以内的短网址。当我们在腾讯、新浪发微博时,有时发很长的网址连接,但由于微博只限制140个字,所以微博就自动把您发的长网址给转换成短网址了。在微博和手机短信提醒等限制字数的地方来使用......
  • 多平台MD5加密与验证的实践:Java、Python、Windows、Linux
    前言在上一篇文章中,我们详细介绍了MD5算法的原理及其应用场景。为了帮助开发者更好地掌握MD5的实际使用方法,本文将通过Java、Python、Windows命令行、以及Linux命令行等多种方式,讲解如何在不同平台上生成和验证MD5摘要。通过这些实例,你可以更灵活地运用MD5来处理数据完整......
  • vivo 全链路多版本开发测试环境落地实践
    作者:来自vivo互联网研发效能团队-WangKang测试环境全链路多版本部署,解决多测试环境资源争抢等问题。一、背景介绍软件系统中全链路指的是从用户请求发起,到最终返回响应的整个过程中所涉及到的所有环节和组件。在微服务软件架构风格盛行的今天,因为微服务独立部署、松耦合等特性,......
  • Python 单元测试详解:Unittest 框架的应用与最佳实践
    Python单元测试详解:Unittest框架的应用与最佳实践文章目录Python单元测试详解:Unittest框架的应用与最佳实践一什么是Unittest1不使用Unittest测试框架2使用Unittest测试框架二unittest使用建议1先写测试case后写测试逻辑2测试文件以_test.py结尾......
  • 大数据新视界 --大数据大厂之Kubernetes与大数据:容器化部署的最佳实践
           ......
  • Android实践:读取和处理SRTM HGT高程数据文件
            在深入探讨如何在Android应用中读取和处理SRTMHGT高程数据文件之前,我们先对SRTM数据及其格式有一个更全面的了解,并详细探讨每一步的实现细节和最佳实践。一、SRTMHGT数据概述        SRTM(ShuttleRadarTopographyMission)是一项由美国宇航局(NASA)......
  • 41. 如何在MyBatis-Plus中实现批量操作?批量插入和更新的最佳实践是什么?
    在MyBatis-Plus中,实现批量操作(如批量插入、批量更新)是非常常见的需求。MyBatis-Plus提供了对批量操作的良好支持,可以通过多种方式实现高效的批量处理。下面详细介绍批量操作的实现方式以及最佳实践。1.批量插入批量插入是指一次性插入多条记录,而不是逐条插入。MyBatis-......
  • 微型导轨在3D打印设备中的应用与实践
    微型导轨的应用范围非常广泛,尤其在追求高精度、高效率及低噪音的现代打印技术中扮演着重要角色。微型导轨在3D打印机等精密设备中是常用元件,以提高打印质量和效率。在打印机中,无论是喷墨式、激光式还是3D打印机,都需要精确的打印头或工作台移动来确保打印质量。微型导轨通过其精密的......
  • 56页PPT | 大数据决策分析平台怎么建设?经典实践方案推荐
    一、现状和目标企业用户现状:数据分散,利用率低,业务需求变化快但IT响应慢。问题:数据展示不及时、不准确,缺乏深入分析工具,报表制作效率低下。目标:建设统一的数据整合平台,消除信息孤岛,将数据转化为信息,提高效能,让数据随需应变。二、平台规划建设该部分深入探讨了如何构建一个稳固且高效......