路由缓存

Spring MVC路由的痛点

传统的Spring MVC中, 当我们的@RequestMapping注解中包含了复杂任何的复杂匹配逻辑(这里的复杂逻辑可以理解为除了一个url对应一个controller实现,并且url中没有*, ? . {foo}等模式匹配的内容)时方能在路由阶段有相对较好的效果,反之如通常情况下一个请求的到来到路由到对应的controller实现这个过程将会是在当前应用中的所有Controller中遍历匹配,值得注意的是通常在微服务提倡RestFul设计的大环境下一个这种遍历几乎是无法避免的, 同时由于匹配的条件本身的复杂性(比如说正则本身为人诟病的就是性能),因此伴随而来的则是SpringMVC的路由的损耗非常的大。

Restlight路由缓存

设计原则

  • 二八原则(80%的业务由20%的接口处理)
  • 算法:类LFU(Least Frequently Used)算法

我们虽然不能改变路由条件匹配本身的损耗, 但是我们希望能做尽量少的匹配次数来达到优化的效果。因此采用常用的"缓存"来作为优化的手段。 当开启了路由缓存后,默认情况下将使用类LFU(Least Frequently Used)算法的方式缓存十分之的Controller,根据二八原则(80%的业务由20%的接口处理),大部分的请求都将在缓存中匹配成功并返回(这里框架默认的缓存十分之一,是相对比较保守的设置)

算法逻辑

当每次请求匹配成功时,会进行命中纪录的加1操作,并统计命中纪录最高的20%(可配)的Controller加入缓存, 每次请求的到来都将先从缓存中查找匹配的Controller(大部分的请求都将在此阶段返回), 失败则进入正常匹配的逻辑。

什么时候更新缓存? 我们不会在每次请求命中的情况下都去更新缓存,因为这涉及到一次排序(或者m次遍历, m为需要缓存的Controller的个数,相当于挑选出命中最高的m个controller)。 取而代之的是我们会以概率的方式去重新计算并更新缓存, 根据2-8原则通常情况下我们当前缓存的内存就是我们需要的内容, 所以没必要每次有请求命中都去重新计算并更新缓存, 因此我们会在请求命中的一定概率条件下采取做此操作(默认0.1%, 称之为计算概率), 减小了并发损耗(这段逻辑本身基于CopyOnWrite, 并且为纯无锁并发编程,本身性能损耗就很低),同时此概率可配置可以根据具体的应用实际情况调整配置达到最优的效果。

配置建议

缓存比例:请求集中化比较高则设置更小(比如集中在1%的Controller上则可以设置为缓存1%) 计算率: 理论上设置的越高实时性越强(缓存更新频率越高)但是并发损耗也会升高,因此建议设置的相对小一些以应对激增的非常用请求即可。