点击「 IT码徒 」, 关注,置顶 公众号
每日技术干货,第一时间送达!
1
什么是缓存预热?
缓存预热是一种在程序启动或缓存失效之后,主动将热点数据加载到缓存中的策略。这样,在实际请求到达程序时,热点数据已经存在于缓存中,从而减少了缓存穿透和缓存击穿的情况,也缓解了SQL服务器的压力。
2
实现
缓存抽象类
首先我们先来实现一个缓存抽象类,这个抽象类的作用就是在将来我们需要将某个模块的数据需要提前加载到缓存中的时候,我们可以创建一个它的实现类,来进行数据的缓存与加载,具体使用方式请看后边我写的例子。
publicabstract classAbstractCache {
/**
* 缓存
*/
protectedabstractvoidinit();
/**
* 获取缓存
*
* @param <T>
* @return
*/
publicabstract <T> T get();
/**
* 清理缓存
*/
publicabstractvoidclear();
/**
* 重新加载
*/
publicvoidreload() {
clear();
init();
}
}
Spring上下文工具类
接下来我们实现一个Spring的上下文工具类,这个工具类需要实现ApplicationContextAware,作用就是负责管理bean的加载与实例化的,具体如何使用,请往下继续阅读。
@Component
public classApplicationContextUtilimplementsApplicationContextAware{
privatestatic ApplicationContext applicationContext;
@Override
publicvoidsetApplicationContext(ApplicationContext applicationContext)throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
/**
* 获取上下文
* @return
*/
publicstatic ApplicationContext getContext(){
return applicationContext;
}
}
缓存预热
然后我们来实现,在程序启动后,直接进行数据的缓存加载,这个类需要实现CommandLineRunner接口,这个接口提供的方法作用就是在程序启动后自动运行。
这个实现类里,我们使用ApplicationContextUtil工具类来获取上下文,然后通过getBeansOfType方法获取实现AbstractCache抽象类的子类,返回的是一个Map类型的集合,接下来通过getBean方法以多态的方式实例化子类,最后我们调用抽象类的init方法即可。
如果有多个实现类,使用@Order注解标注先后运行就可以了。
@Component
@ConditionalOnProperty(name = {"cache.init.enable"}, havingValue = "true", matchIfMissing = false)
public classCachePreheatHandlerimplementsCommandLineRunner{
/**
* 缓存预热
* @param args
* @throws Exception
*/
@Override
publicvoidrun(String... args)throws Exception {
ApplicationContext context = ApplicationContextUtil.getContext();
Map<String, AbstractCache> beansOfType = context.getBeansOfType(AbstractCache. class);
for (Map.Entry<String, AbstractCache> cacheEntry : beansOfType.entrySet()) {
AbstractCache cache = context.getBean(cacheEntry.getValue().get class());
cache.init();
}
}
}
解释:
@ConditionalOnProperty这个注解在这里的作用是,需要在配置文件开启cache.init.enable,理想值是true,默认值是false。
cache.init.enable=true
使用
我们就以新闻热点为例,数据库中有一张tb_news新闻表,均为微博热搜体育榜内容。
接下来创建一个AbstractCache的实现类,来实现具体的实现
@Component
@RequiredArgsConstructor
public classNewsCacheextendsAbstractCache{
privatestaticfinal String NEWS_KEY = "news";
privatefinal RedisTemplate<String, Object> redisTemplate;
privatefinal NewsService newsService;
@Override
protectedvoidinit(){
if (Boolean.FALSE.equals(redisTemplate.hasKey(NEWS_KEY))) {
redisTemplate.opsForValue().set(NEWS_KEY, newsService.list(), 30, TimeUnit.MINUTES);
}
}
@Override
public <T> T get(){
if (Boolean.FALSE.equals(redisTemplate.hasKey(NEWS_KEY))) {
reload();
}
return (T) redisTemplate.opsForValue().get(NEWS_KEY);
}
@Override
publicvoidclear(){
redisTemplate.delete(NEWS_KEY);
}
}
然后启动项目,我们就发现,Redis中已经存好了热点数据
最后可以通过get方法获取数据了,也不用担心数据过期了。
@RestController
@RequestMapping("/news")
@RequiredArgsConstructor
public classNewsController{
privatefinal NewsCache newsCache;
@GetMapping("/cache")
public List<News> list(){
return newsCache.get();
}
}
好了,小伙伴们,今天的分享就到此结束了,欢迎留出建议,如果觉得内容可以,还请来个点赞和关注吧!
来源:juejin.cn/post/7287907117336526863
— END —
PS:防止找不到本篇文章,可以收藏点赞,方便翻阅查找哦。
往期推荐