分享更有价值
被信任是一种快乐

SpringCache如何实现请求级别缓存

文章页正文上

这篇文章将为大家详细讲解有关SpringCache如何实现请求级别缓存,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
要将数据缓存在一次请求周期内,那我们先得区分是什么环境下的请求,以分析我们如何存储数据。
Web环境下的有个绝佳的数据存储位置HttpServletRequest的Attribute。调用setAttribute和getAttribute方法就能轻易地将我们的数据用key-value的形式存储在请求上,而且每次请求都自动拥有一个干净的Request。想要获取到HttpServletRequest也非常简单,在web请求中随时随地调用((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()即可。
我司所使用的rpc框架是基于finagle自研的,对外提供服务时使用线程池进行处理请求,即对于一次完整的请求,会使用同一个线程进行处理。首先想到的办法还是改动这个rpc框架服务端,增加一个可以对外暴露的、可以key-value存储的请求上下文。为了能在方便的地方获取到这个请求上下文,得将其存储在ThreadLocal中。
综合这两种环境考虑,我们最好还是实现一个统一的方案以减少维护和开发成本。Spring的RequestContextHolder.getRequestAttributes()其实也是使用ThreadLocal来实现的,那我们可以统一将数据存到ThreadLocal

>,自己来维护缓存的清理。

存储位置有了,接下来实现SpringCache思路就比较清晰了。
要实现SpringCache需要一个CacheManager,接口定义如下
可以看到其实只需要实现Cache接口就行了。在上一篇文章中提到的SimpleCacheManager,它的Cache实现ConcurrentMapCache内部的存储是依赖ConcurrentMap。我们的实现跟它非常类似,最主要的不同是我们需要使用ThreadLocal

>下面给出几处关键的实现,其他部分简单看下ConcurrentMapCache就能明白。

我们选择不直接继承Cache而是AbstractValueAdaptingCache,其被大多数缓存实现所继承,它的作用主要是包装value值以区分是没有命中缓存还是缓存的null值。
我们的缓存数据存储的地方,ThreadLocal保证缓存只会存在于这一个线程中。同时又因为只有一个线程能够访问,我们简单地使用HashMap即可。
至此我们即将大功告成,只差一个步骤,ThreadLocal的清理:使用AOP实现即可。
记得将Cache的clear方法通过我们自定义的CacheManager暴露出来。同时也要确保切面能覆盖每个请求的结束。
从以上一个简单的ThreadLocalCacheManager实现,我们对CacheManager又有了更多免费云主机、域名的理解。
同时可能也会有更多的疑问。
再回顾Spring Cache为我们提供的@Cacheable中的sync的注释,它提到此功能的作用是: 同步化对被注解方法的调用,使得多个线程试图调用此方法时,只有一个线程能够成功调用,其他线程直接取这次调用的返回值。同时也提到这仅仅只是个hint,是否真的能成还是要看缓存提供者。
我们找到Spring Cache处理缓存调用的关键方法org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, java.lang.reflect.Method, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)(spring-context-5.1.5.RELEASE)
经过分析,当sync = true时, 只会调用如下代码
即我们上文实现的T get(Object key, Callable valueLoader)方法,回头一看一切都清晰了。只要我们的this.store.get().computeIfAbsent是同步的,那这个sync = true就起作用了。当然我们这里使用的HashMap不支持,但是我们如果换成ConcurrentMap就能够实现同步化的功能。另外简单粗暴地让方法同步也是可以的(RedisCache就是这样做的)。
当sync = false时,会组合Cache中其他的方法进行缓存的处理。逻辑较为简单清晰,自行阅读源码即可。
异步操作分两种情况,直接创建线程或者使用线程池。对于第一种情况我们可以简单地使用java.lang.InheritableThreadLocal来替代ThreadLocal,创建的子进程会自然而然地共享父进程的InheritableThreadLocal;第二种情况就相对比较复杂了,建议可以参考alibaba/transmittable-thread-local,它实现了线程池下的ThreadLocal值传递功能。关于“SpringCache如何实现请求级别缓存”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

相关推荐: jquery中document指的是什么

本篇内容主要讲解“jquery中document指的是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“jquery中document指的是什么”吧! 在jquery中,document是文档的意思,代表浏览器窗口…

文章页内容下
赞(0) 打赏
版权声明:本站采用知识共享、学习交流,不允许用于商业用途;文章由发布者自行承担一切责任,与本站无关。
文章页正文下
文章页评论上

云服务器、web空间可免费试用

宝塔面板主机、支持php,mysql等,SSL部署;安全高速企业专供99.999%稳定,另有高防主机、不限制内容等类型,具体可咨询QQ:360163164,Tel同微信:18905205712

主机选购导航云服务器试用

登录

找回密码

注册