概述 本文针对实际业务开发过程中遇到的 热点数据缓存 问题,引入了 caffeine 中的 LoadingCache 对象,并分析了具体的使用场景,最后将使用的完整过程中的关键代码罗列出来,具体内容如下 caffeine 说明和 maven 依赖 使用场景分析 缓存对象的初始化 缓存数据的加载 缓存的使用 caffeine 说明和 maven 依赖caffeine 提供了基于 内存 的缓存机制,没有持久化到磁盘的功能,意味着一旦应用关闭或者重启,内存中的缓冲对象需要全部重新加载 maven 依赖如下:使用场景分析数据存在数据库中,在短期内会大量访问,比如 10qps,减少对数据库的查询,提高响应速度,提高用户体验 数据不会频繁变化,修改后期望 1 s 后缓存中的数据失效 可以限制数据库的访问频率,比如最大每秒一次(只要缓存不失效就不会访问数据库) 需要缓存的数据量不能太大 缓存对象的初始化通过@PostConstruct修饰的方法来初始化缓存对象SwitchCache 通过 @PostConstruct 修饰的方法来初始化缓存对象 通过 expireAfterWrite(1, TimeUnit.SECONDS) 设置缓存写入后 1s 后失效,相当于限制了最多每秒访问一次数据库 package com.ckjava.kqc.kone.cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.LoadingCache; import com.ckjava.kqc.kone.model.entity.others.KoneSwicth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * Function: 开关缓存 * * @author chenkui * @date 2021/8/19 */ @Component public class SwitchCache { private static LoadingCache com.github.ben-manes.caffeine caffeine2.8.1 switchCache; private static final Logger logger = LoggerFactory.getLogger(SwitchCache.class); @Autowired private ApplicationContext mApplicationContext; @PostConstruct private void init() { switchCache = CacheBuilder.newBuilder() .expireAfterWrite(2, TimeUnit.SECONDS) .build(new SwitchLoader(mApplicationContext)); } public KoneSwicth get(final String switchKey) { try { System.out.println(switchCache.stats().toString()); return switchCache.get(switchKey); } catch (final ExecutionException e) { logger.error("SwitchCache get has error", e); return null; } } } 通过@Configuration创建 bean 初始化 LoadingCache 对象 或者配置到 Configuration 中,如下 CacheConfig: 缓冲对象初始化 import java.util.concurrent.TimeUnit; /** * Function: 缓存 * * @author chenkui * @date 2021/8/19 */ @Configuration public class CacheConfig { @Bean @Primary public LoadingCache switchCache( final ApplicationContext mApplicationContext) { return CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.SECONDS) .build(new SwitchCacheLoader(mApplicationContext)); } } SwitchCacheCom:缓冲方法封装 import com.google.common.cache.LoadingCache; import com.ckjava.kqc.kone.model.entity.others.KoneSwicth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Function: 开关缓存 * * @author chenkui * @date 2021/8/19 */ @Component public class SwitchCacheCom { private static final Logger logger = LoggerFactory.getLogger(SwitchCacheCom.class); @Autowired private LoadingCache switchCache; public KoneSwicth get(final String switchKey) { try { final KoneSwicth koneSwicth = switchCache.get(switchKey); logger.info(switchCache.stats().toString()); return koneSwicth; } catch (final Exception e) { logger.error("SwitchCache get has error", e); return null; } } } 缓存数据的加载SwitchLoader:用于在缓存中的数据失效后,再从数据库中获取,并自动存储到缓存中 package com.ckjava.kqc.kone.cache; import com.google.common.cache.CacheLoader; import com.ckjava.kqc.kone.dao.others.KoneSwicthMapper; import com.ckjava.kqc.kone.model.entity.others.KoneSwicth; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; /** * Function: switch 加载 * * @author 执剑 * @date 2021/8/19 */ public class SwitchLoader extends CacheLoader { private static final Logger logger = LoggerFactory.getLogger(SwitchLoader.class); private final KoneSwicthMapper mKoneSwicthMapper; public SwitchLoader(final ApplicationContext applicationContext) { mKoneSwicthMapper = applicationContext.getBean(KoneSwicthMapper.class); } @Override public KoneSwicth load(final String switchKey) { logger.info("loading from db"); return mKoneSwicthMapper.findByUniq(KoneSwicth.builder().switchKey(switchKey).build()); } } 缓存的使用SwitchController:通过接口请求的时候直接使用缓存对象 import com.ckjava.kqc.kone.annotation.KoneResponseResult; import com.ckjava.kqc.kone.cache.SwitchCacheCom; import com.ckjava.kqc.kone.config.pattern.ApiPatterns; import com.ckjava.kqc.kone.model.entity.others.KoneSwicth; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author chenkui * @date 10:39 2021/8/19 */ @Api(tags = "系统开关") @RestController @RequestMapping(value = ApiPatterns.PREFIX + "/switch") public class SwitchController { @Autowired private SwitchCacheCom mSwitchCache; /** * 返回业务线列表 */ @ApiOperation(value = "获取开关") @KoneResponseResult("获取开关!") @GetMapping() public KoneSwicth getSwitch( @RequestParam final String switchKey) { return mSwitchCache.get(switchKey); } }