我们开始设计一下这个限流框架,我们先对框架所需要的非功能性的设计思路来讲解,如何通过设计,来实现一个满足易用,易于扩展,灵活,低延时,高容错的非功能性需求的限流框架
限流的规则
定义限流规则包含的语法格式有,调用方,接口,限流阈值,时间粒度这几个元素,框架用户按照这个语法格式来限制流量,比如下面的
configs:
– appId: app-1 limits: – api: /v1/user limit: 100 unit:60 – api: /v1/order limit: 50 – appId: app-2 limits: – api: /v1/user limit: 50 – api: /v1/order limit: 50 |
unit表示事件粒度,默认为1秒,limit表示unit时间粒度内最大允许的请求次数,上面就是app-1的实例对接口 /v1/user每60秒的请求次数不能超过100次
对于限流时间的粒度设置,我们可以限制到1秒钟不超过1000次,也可以设置10毫秒不超过10次,也可以设置1分钟不超过6万次,虽然这几种限流规则是等价的,但是不同的时间粒度会有不同的限制效果,比如可能6万次效果集中在1秒钟到达,这样就没有保护的作用了,相反,因为接口访问在细时间内的事件效果很随机,过小的时间粒度,会误杀掉很多不应该限流的请求,所以,并非粒度越小越好
而且,因为Spirng支持各种各样的配置文件,比如XML,YAML,Properties等,而且,在Spring中,约定优于配置,Spring框架用户只要按照配置文件来命名,放在约定的路径下,Spring框架就能按照约定自动查找并加载配置文件
在限流框架中,我们延续了Spring的配置方法,支持XML YAML Properties等配置文件格式,约定默认的文件为ratelimter-rule.yaml,放在classpath路径中
而且,最好还能支持从其他数据源获取配置的方式,比如Zookeeper,或者自定义的配置中心
限流算法:
常见的限流算法有:固定时间窗口限流算法,滑动时间窗口限流算法,令牌桶限流算法,漏桶限流算法,比如固定时间窗口限流算法,我们只需要选定一个起始时间,然后后来每一个请求,都给计数器加一,如果在当前时间窗口内,根据限流规则,累加访问次数超过了限流值,就会触发熔断,拒绝接口请求,然后到了下一个时间窗口,计数器重新清零技术
但是,固定时间窗口的限流算法缺点很明显,太过于粗鲁,无法应对两个时间窗口临界时间之间的突发流量,我们限流是每秒100次接口的请求,在第一个秒内,100次集中在最后10毫秒,第二个秒内,100次请求都在最开始的10毫秒内,这样都符合了限流的要求,但是这200次请求,集中在临界点,可能导致击穿系统
为了让流量更加的平滑,我们就有了更加高级的滑动时间窗口限流算法,令牌桶算法和漏桶限流算法
尽管,固定时间窗口限流算法没法做到流量的平滑,但是大部分时间内,已经够用了,默认情况下,框架使用固定时间窗口限流算法做限流,但是留好了扩展点,方便扩展其他的限流算法,除此外,提高框架的可用性,将其他的限流算法,最好也在框架中实现出来,供框架用户根据业务场景自己选择
限流模式
我们再说下限流模式,将限流分为了两种,单机限流和分布式限流,单机限流:是针对单个实例的访问频率进行限制,分布式限流,就是针对某个服务的多个实例的总访问频率进行限制
假设,我们开发了一个用户相关的问服务,我们部署了5个实例,可以限制某个调用方,对单个实例的某个接口的访问频率,不能超过100次/秒,这就是单机限流,我们限制某个调用方,对5个实例的某个接口总访问频率,不能超过500次/秒,这就是分布式限流
从实现的角度来说,单机限流和分布式限流的主要区分在于接口访问计数器的实现,单机限流只需要在单个实例中维护自己的接口计数器,而分布式限流则需要集中管理计数器,比如利用一个Redis来实现计数器的管理,做到多个实例对同一个计数器累加计数,从实现对多个实例总访问频率的限制
而整合了Redis,就必须要考虑Reids的一些问题,不能因为限流框架的问题,导致本身应用的可用性,而且框架做到低延迟,限流逻辑不能占用太长的时间,不能或者影响到请求接口本身的响应时间,分布式限流依赖于Redis,于是我们就需要对Redis进行一些优化处理
对于Redis本身的一些异常,我们就需要进行一些处理,捕获为统一的异常,向上抛出或者吞掉就可以了,但是Redis可能存在访问超时,从而严重影响到接口的响应时间,所以,在获取Reids实例的时候,我们需要设置合理的访问时间,一旦超时,就判定为限流失效,继续接收接口请求,Redis的超时访问时间既不能太大也不能太小,太大可能影响到接口的响应时间,太小可能导致太多的限流失效,只能考虑通过压测或者线上监控,从而获取到Redis的访问时间分布时间,从而设置Reids的超时间
集成使用
剖析Spring框架的时候,说到了低侵入松耦合的设计思想,限流框架应该做到这个设计思想,框架是集中到应用中使用的,我们希望框架尽可能的地侵入,和业务代码松耦合,替换和删除更加容易
Mybatis框架,是为了简化数据库编程,为了进一步简化开发,Mybatis提供了Mybatis-Spring的类库,方便使用了Spring框架项目中继承Mybatis框架,我们也可以借鉴Mybatis-Sping,方便使用Spring的项目集成限流框架,将易用性做到了极致
本章重点:
我们将框架的整体划分为了限流规则,限流算法,限流模式,集成使用者这四个模块来分析讲解,我们说了如何满足易用性,灵活,易扩展,低延迟,高容错等非功能性需求
针对限流规则,我们尽可能的使用约定优于配置,借鉴Spring的配置方式,支持XML,YAML,Properties等配置文件格式,只要配置文件名字对,就可以自动的查找和加载配置文件
而且,我们还可能支持从其他的数据源获取配置的方式,比如Zookeeper,或者自己的配置中心
针对限流算法,我们使用默认的固定时间窗口限流算法做限流工作,不过考虑到扩展性,我们要预留扩展点,并且对于主流的限流算法,我们可以再框架中实现出来,供用户自己选择使用
针对限流模式,在分布式限流中,使用了外部存储Redis,因为通信成本高,所以对于Redis的异常,访问超时,进行了针对化的优化
针对集成使用,我们希望低侵入,和业务代码松耦合,应用继承框架的代码,尽可能的集中,不分散,而且对于易用性,可以借鉴Mybatis-Spring类库,实现一个RateLimiter-Spring类库,方便Spring框架的应用集成限流框架
课后思考:
对于限流规则,时间粒度不能太大,也不能太小,限流值要设置的合理,太大的话起不到限流的作用,太小容易误杀,如何选择呢?
对于限流的框架使用,高流量不可怕,就怕暴涨击穿的问题,对于可以预测的平稳访问,设置的粒度大点无可厚非,对于可能出现的一些短时高流量服务,可以设置的粒度低些,当然,在实际开发中,我们还有各种缓存服务器,CDN帮助我们开发,避免流量过大问题