基于SpringBoot版本:2.1.4.RELEASE
问:为什么使用二级缓存?
☛☛ Redis和Ehcache同为内存存储,但是Redis需要走网络,而Ehcache是本地jvm缓存,速度上Ehcache会更快
☛☛ 考虑到Redis极端情况下会出现雪崩,比如多个key在同一段时间内失效,请求直接怼到数据库,可能导致服务雪崩,为了 避免这种情况发生,最好的解决方案就是使用二级缓存
问:Redis和Ehcache谁作为一级缓存比较合适?
☛☛ Ehcache是走本地缓存,速度肯定高于Redis,所以使用Ehcache作为一级缓存,完美
STEP ONE:SpringBoot整合Redis
这里的内容参考博客:SpringBoot整合Redis
STEP TWO:SpringBoot整合Ehcache
☛☛ 导入依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
☛☛ ehcache.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 指定一个文件目录,当EhCache把数据写到硬盘上时,将把数据写到这个文件目录下 -->
<diskStore path="java.io.tmpdir" />
<!-- name 缓存名称
maxElementsInMemory 设置基于内存缓存可存放对象的最大数目
maxElementsOnDisk 在磁盘上缓存的element的最大数目,默认值为0,表示不限制
eternal 如果为true,表示对象永远不会过期 ,false 时为timeToldleSeconds和timeToLiveSeconds
timeToldleSeconds 对象允许处于空闲的最长时间
timeToLiveSeconds 即缓存自创建日期起能够存活的最长时间
overflowToDisk 如果内存中数据超过内存限制,是否要缓存到磁盘上 true表示存在硬盘上
memoryStoreEvictionPolicy 缓存对象清除策略 以下为三种策略 FIFO 先进先出 LFU 一直以来最少被使用策略 LRU 最近最少被使用
-->
<!-- 默认缓存 -->
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
<!--
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个
userCache:自定义的缓存,缓存用户信息
-->
<cache name="userCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</cache>
</ehcache>
☛☛ application.properties配置Ehcache
spring.cache.ehcache.config=classpath:ehcache.xml
spring.cache.type = ehcache
☛☛ EhcacheService
public interface EhcacheService {
// 获取缓存
public Object get(String cacheName, String key);
// 放入缓存
public void put(String cacheName, String key, Object value);
// 清空缓存
public void evict(String cacheName, String key);
}
☛☛ EhcacheServiceImpl
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.stereotype.Service;
import com.mote.service.EhcacheService;
@Service("ehcacheService")
public class EhcacheServiceImpl implements EhcacheService {
@Autowired
private EhCacheCacheManager cacheCacheManager;
// 获取缓存
public Object get(String cacheName, String key) {
Cache cache = cacheCacheManager.getCacheManager().getCache(cacheName);
Element element = cache.get(key);
return element == null ? null : element.getObjectValue();
}
// 放入缓存
public void put(String cacheName, String key, Object value) {
Cache cache = cacheCacheManager.getCacheManager().getCache(cacheName);
Element element = new Element(key, value);
cache.put(element);
}
// 清空缓存
public void evict(String cacheName, String key) {
Cache cache = cacheCacheManager.getCacheManager().getCache(cacheName);
cache.remove(key);
}
}
案例代码:根据ID查询一个用户信息
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.mote.entity.User;
import com.mote.service.EhcacheService;
import com.mote.service.RedisService;
import com.mote.service.UserService;
@RestController
public class UserController {
@Autowired
private UserService userService;
@Autowired
private RedisService redisService;
@Autowired
private EhcacheService ehcacheService;
public static final String CACHE_NAME = "userCache";
@GetMapping("/user/get/{id}")
public User getUser(@PathVariable("id") int id) {
try {
// 定义key
String key = "key_" + id;
// 从一级缓存中获取
User euser = (User) ehcacheService.get(CACHE_NAME, key);
if (euser != null)
return euser;
// 从二级缓存中获取
User ruser = (User) redisService.get(key);
if (ruser != null) {
// 放入一级缓存
ehcacheService.put(CACHE_NAME, key, ruser);
return ruser;
}
// 从数据库查询
User user = userService.getUser(id);
// 放入一级缓存
ehcacheService.put(CACHE_NAME, key, user);
// 放入二级缓存
redisService.set(key, user);
return user;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}