赞
踩
最近在优化项目代码,发现了有一个接口A执行的时候会频繁调用另一个获取用户信息的接口B。虽然接口B加了redis缓存,但是由于redis服务器是单独部署,频繁的I/O请求仍然对接口响应性能有影响,于是想到了加二级缓存,这样接口A调用接口B的时候可以把重复的用户信息暂存,进而提高性能。
二级缓存很多,我选择了ehcache。在网上搜了一些教程,发现配置非常简单,于是立刻着手做,然而过程其实并不顺利,踩了一些坑,这里记录下。
1、pom引入。注意<version>标签,如果是springboot的话应该会自动匹配版本号,也可以自己填写2.10.6之类的。
- <dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache</artifactId>
- <version>${ehcache.version}</version>
- </dependency>
2、在resources目录下创建一个ehcache.xml配置文件,该文件存储了ehcache相关的配置,示例如下。我这里配置了一个名叫gatewayUser的缓存,过期时间设置为2分钟,下面会用到。
- <?xml version="1.0" encoding="UTF-8"?>
- <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
-
- <diskStore path="java.io.tmpdir"/>
-
- <!-- 设定缓存的默认数据过期策略 -->
- <defaultCache
- maxElementsInMemory="10000"
- eternal="false"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- overflowToDisk="true"
- maxElementsOnDisk="10000000"
- diskPersistent="false"
- diskExpiryThreadIntervalSeconds="120"
- memoryStoreEvictionPolicy="LRU"/>
-
- <!-- 自定义的缓存 -->
- <cache name="gatewayUser"
- maxElementsInMemory="1000"
- eternal="false"
- timeToIdleSeconds="120"/>
-
- </ehcache>
这里有一个小点需要注意:
<ehcache>标签中的
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
可以不加,没什么影响。如果加了发现飘红,可采用以下方式处理Idea 使用ehcache.xml 出现异常_OY..的博客-CSDN博客
3、在Application启动类上添加@EnableCaching注解
4、在方法上加注解:
注意这里的value一定和xml配置文件中的名字相同。比如这里的value为“gatewayUser”,则对应的就是ehcache.xml文件中自定义的“gatewayUser”配置,表示该方法的缓存都会使用同一个配置,且都会放在同一个缓存中。key表示每个缓存的唯一标识,这里用userId就可以了。
特别注意,为了使得@Cacheable生效,方法必须是public;如果同一个类中调用,还必须注入当前类,这样才能触发AOP。
网上很多资料到这里基本就结束了,但如果你运行程序并调用该方法,会报错,类似这种:
- {
- "code": 8000,
- "data": [],
- "message": "未知错误java.lang.IllegalArgumentException: Cannot find cache named 'gatewayUser' for Builder......
- }
程序提示没有找到相应的cache,如果在源码上打断点,会发现确实没有正确的加载缓存配置:
这里有人会说需要在application.yml中加以下配置:
spring.cache.ehcache.config=classpath:ehcache.xml
但其实加了仍旧会报错!因为ehcache默认的会去resource目录下找ehcache.xml配置,所以这里加不加无所谓。其实这里是少了一个配置类。
5、(重要)再写一个cacheManager配置类:
- package ......
-
- import org.springframework.cache.CacheManager;
- import org.springframework.cache.annotation.EnableCaching;
- import org.springframework.cache.ehcache.EhCacheCacheManager;
- import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.io.ClassPathResource;
-
-
- @Configuration
- @EnableCaching
- public class EhCacheConfig {
-
- //需要生成一个缓存管理器
- @Bean
- public CacheManager cacheManager() {
- net.sf.ehcache.CacheManager ehCM = ehCacheCacheManager().getObject();
- CacheManager cacheManager = new EhCacheCacheManager(ehCM);
- return cacheManager;
- }
-
- //缓存管理器由该工厂Bean生成
- @Bean
- public EhCacheManagerFactoryBean ehCacheCacheManager() {
- EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
- cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));//工厂去读配置文件
- cmfb.setShared(true);
-
- return cmfb;
- }
- }
再次运行程序,发现缓存已生效!
到这里ehcache缓存已经能够正常工作了。
在方法内部我们仍旧使用redis进行缓存。这样就实现了二级缓存,程序性能又能提升啦。
- @Cacheable(value="gatewayUser", key="#userId")
- public GatewayUser getUserInfoByUserId(Integer userId) {
- String key = "userinfo:" + userId.toString();
- //尝试从redis缓存中获取
- String userstr = redis.opsForValue().get(key);
- GatewayUser gatewayUser = null;
- if (StringUtils.isNotBlank(userstr)) {
- return JSON.parseObject(userstr, GatewayUser.class);
- } else {
- //到数据库中查询
- //将查询结果插入redis缓存中
- redis.opsForValue().set(key, reportStat);
- redis.expire(key, 3600 * 48, TimeUnit.SECONDS);
-
- return gatewayUser;
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。