服务治理,管理微服务,保证平台整体正常、平稳的运行。服务治理涉及鉴权、限流、降级、熔断、监控告警等。
鉴权
不同的接口提供给不同的应用,并不是所有应用都可以访问该服务,也不是每个有访问权限的应用都可以访问该服务的所有接口。
要实现接口鉴权功能,需要事先将应用对接口的访问权限规则设置好。当某个应用访问其中一个接口的时候,可以拿应用的请求URL,在规则中进行匹配。如果匹配成功,就说明允许访问;如果没有可以匹配的规则,那就说明这个应用没有这个接口的访问权限,就拒绝服务。
接口格式格式:类似Dubbo的RPC接口,类似Spring Cloud的HTTP接口。
精确匹配规则
只有当请求URL跟规则中配置的某个接口精确匹配时,这个请求才会被接受、处理。
不同的应用对应不同的规则集合。可以采用散列表来存储这种对应关系。
将每个应用对应的权限规则,存储在一个字符串数组中。当用户请求到来时,拿用户的请求URL,在这个字符串数组中逐一匹配,匹配的算法对应字符串匹配算法(KMP、BM、BF)。
规则不会经常变动,可以按照字符串的大小给规则排序,把它组织成有序数组这种数据结构,加快匹配速度。当要查找某个URL能否匹配其中某条规则的时候,可以采用二分查找算法,在有序数组中进行匹配。
前缀匹配规则
只要某条规则可以匹配请求URL的前缀,这个请求就可以被接受处理。
不同的应用对应不同的规则集合。同样采用散列表来存储这种对应关系。
将每个应用的规则集合,组织成Trie树这种数据结构。Trie树非常适合用来做前缀匹配。Trie树中的每个节点不是存储单个字符,而是存储接口被“/”分割之后的子目录。
规则并不会经常变动,在Trie树中,可以把每个节点的子节点门,组织成有序数组这种数据结构。当在匹配的过程中,可以利用二分查找算法,决定从一个节点应该跳到哪一个子节点。
模糊匹配规则
规则中包含通配符(“**”匹配任意多个子目录,“*”匹配任意一个子目录),只要请求URL可以跟某条规则模糊匹配,就可以被接受处理。
不同的应用对应不同的规则集合。依然采用散列表来存储这种对应关系。
- 借助正则表达式的解决思路,采用回溯算法,拿请求URL跟每条规则逐一进行模糊匹配。
- 并不是每条规则都包含通配符,包含通配符的只是少数。把不包含通配符的规则和包含通配符的规则分开处理。
把不包含通配符的规则,组织成有序数组或者Trie树。剩下的少数包含通配符的规则,简单存储在一个普通数组中。
当接收到一个请求URL之后,可以先在不包含通配符的有序数组或者Trie树中查找。如果能够匹配,就不需要继续在通配符规则中匹配;如果不能匹配,就继续在通配符规则中查找匹配。
限流1
对接口调用的频率进行限制。例如每秒钟不能超过100次调用,超过之后就拒绝服务。
按照不同的限流粒度,限流可以分为很多种类型。给每个接口限制不同的访问频率;给所有接口限制总的访问频率;限制某个应用对某个接口的访问频率等。
固定时间窗口限流算法
首先需要选定一个时间起点,之后每当有接口请求到来,就将计数器加一。如果在当前时间窗口内,根据限流规则,出现累加访问次数超过限流值的情况时,就拒绝后续的访问请求。当进入下一个时间窗口之后,计数器就清零重新计数。
缺点:限流策略过于粗略,无法应对两个时间窗口临界时间内的突发流量。
滑动时间窗口限流算法
限制任意时间窗口内,接口请求数都不能超过某个阈值。从流量曲线上来看会更加平滑。
假设限流的规则为,在任意1s内,接口的请求次数都不能大于K次。就维护一个大小为K+1的循环队列,用来记录1s内到来的请求。(循环队列的大小等于限流次数加一,循环队列存储数据时浪费一个存储单元)
当有新的请求到来时,将与这个新请求的时间间隔超过1s的请求,从队列中删除,然后再来开队列中是否有空闲位置,如果有,则把新请求存储在队列尾部(tail指针所指的位置);如果没有,则说明这1s内的请求次数已经超多了限流值K,这个请求被拒绝服务。
缺点:滑动时间窗口限流算法,依然不能防止,在细粒度上访问过于集中的问题。
其他:
- 维护成优先级队列(根据请求时间构建小顶堆),最早的请求时间放在堆顶。当有新的请求进来时相当于在小顶堆内插入数据,判断此时跟堆顶的时间差是否小于1s,并且堆的大小小于请求限制次数。每次插入数据时,删除1s外的数据,重新排序,确定新的堆顶。时间复杂度较高。
令牌桶算法
基于时间窗口的限流算法,不管是固定时间窗口还是滑动时间窗口,只能在选定的时间粒度上限流,对选定时间粒度内的更加细粒度的访问频率不做限制。无法应对细时间粒度的突发流量。
- 接口限制t秒内最大访问次数为n,则每隔t/n秒会放一个token到桶中;
- 桶中最多可以存放b个token,如果token到达时令牌桶已经满了,那么这个token会被丢弃;
- 接口请求会先从令牌桶中取token,拿到token则处理接口请求,拿不到token则执行限流。
改进思路:
- 预热桶
- 一次性放入多个令牌
不需要专门起一个线程每隔固定时间放token到桶中。每次在取token之前,根据上次放入token的时间戳和现在的时间戳,计算出这段时间需要放多少token进去,一次性放进去。
- 支持一次性取多个令牌
缺点:对于没有预热的令牌桶,做否决式限流会导致误杀很多请求。间隔一定时间才向桶中放入一个令牌,但接口的访问在1s内的随机性很强。
漏桶算法
漏桶算法相当于令牌桶算法的改进。
对于取令牌的频率也有限制,要按照t/n固定的速度来取令牌。漏桶算法对流量的整形效果更加好,流量更佳平滑,任何突发流量都会被限流。
总结延伸
令牌桶和漏桶算法比较适合阻塞式限流,超过最大访问频率后,请求不会被拒绝,而是被阻塞到有令牌后再继续执行。
对响应时间比较敏感的限流场景(微服务),适合选择基于时间窗口的否决式限流算法。滑动时间窗口限流算法空间复杂度较高,内存占用较多。固定时间窗口算法适合微服务接口限流场景,简单、性能好、不易出错。