课程标题《基于装饰模式手写多级缓存框架》
课程内容:
1.基于装饰模式手写多级缓存框架演示
2.一级与二级缓存基本的概念
3.手写模拟一级与二级缓存基本概念
4.装饰模式基本架构设计原理
5.基于装饰模式重构设计多级缓存
6.什么是外观模式?外观模式重构原理
20点25分准时开始
一级与二级、三级缓存基本的概念
jvm缓存(一级缓存)
优点:查询速度非常快
缺点:
1.jvm内置缓存与业务申请内存没有解耦,有可能会发生内存溢出问题
2.缓存容量比较少的
3.jvm内置缓存集群的话 需要考虑数据一致性问题
4.jvm内存缓存 jvm服务器重启时 内存数据都没有了
缓存—当缓存满了 缓存淘汰策略 将近期没有使用的缓存 自动清理掉
jvm内置缓存:EhCache 、OSCache 底层就是一个map集合
触发gc回收时 ,stw 短暂暂停用户线程 会形式短暂卡顿问题
将数据存放在内存—将对象序列化 json
从内存中读取数据到程序中(反序列化 json数据变成对象)
将数据存放在内存、redis(内存)、数据库
redis缓存(二级缓存)
优点:查询数据存放内存中,缓存数据非常多 redis集群
缺点:但是查询过程中 需要经过网络 IO操作
数据库(三级)
优点:存放数据内容比redis更多 更加可靠
缺点:数据直接持久化在硬盘中,查询效率偏低 会经过磁盘io
在实际开发项目,为了减少数据库的访问压力,我们都会将数据缓存到内存中,比如:Redis(分布式缓存)、EhCache(JVM内置缓存)。
缓存级别越小缓存内容越少,缓存级别越大缓存内容越多;
例如在早期项目中,项目比较小可能不会使用Redis作为缓存,使用JVM内置的缓存框架,项目比较大的时候开始采用Redis分布式缓存框架,这时候需要设计一级与二级缓存。
缓存机制
JVM内置缓存:将数据缓存到当前JVM中
缺陷:占用当前JVM内存空间,可能造成内存溢出问题;集群很难保证各个节点之间数据同步问题。
举例:EhCache,OsCache底层原理采用HashMap实现 淘汰策略
分布式缓存Redis:数据能够共享
装饰模式概念
不改变原有的代码实现增强。Mybatis、Hibernate二级缓存都属于开发者自己去实现扩展的功能。
装饰模式与代理模式区别
代理模式对目标对象(目标方法)实现增强;
装饰模式对装饰对象实现增强,不能改变原有代码。
基于HashMap手写Jvm内置缓存
package com.mayikt.utils;
import com.alibaba.fastjson.JSONObject;
import com.mayikt.entity.UserEntity;
import java.util.HashMap;
public class JvmMapCacheUtils<V> {
private static HashMap<String, String> cacheMap = new HashMap<String, String>();
public static void putEntity(String key, Object object) {
cacheMap.put(key, JSONObject.toJSONString(object));
}
public static <T> T getEntity(String key, Class<T> t) {
String json = cacheMap.get(key);
JSONObject jsonObject = JSONObject.parseObject(json);
return JSONObject.parseObject(json, t);
}
public static void main(String[] args) {
UserEntity userEntity = new UserEntity(1, "mayikt", 22);
JvmMapCacheUtils.putEntity("user1", userEntity);
UserEntity user = JvmMapCacheUtils.getEntity("user1", UserEntity.class);
System.out.println(user);
}
}
模拟一级与二、三级缓存概念
package com.mayikt.service;
import com.alibaba.fastjson.JSONObject;
import com.mayikt.entity.UserEntity;
import com.mayikt.mapper.UserMapper;
import com.mayikt.utils.JvmMapCacheUtils;
import com.mayikt.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 余胜军
* @ClassName MemberService
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@RestController
public class MemberService {
@Autowired
private UserMapper userMapper;
@RequestMapping("/getMember")
public UserEntity getMember(Integer id) {
// 1.先查询一级缓存(jvm内置缓存)
String cacheKey = "com.mayikt.service.MemberService.getMember()Integer=" + id;
UserEntity jvmUserEntity = JvmMapCacheUtils.getEntity(cacheKey, UserEntity.class);
if (jvmUserEntity != null) {
return jvmUserEntity;
}
// 2.如果一级缓存没有 在查询 二级缓存(redis)
String redisJson = RedisUtil.getString(cacheKey);
if (!StringUtils.isEmpty(redisJson)) {
UserEntity redisUserEntity = JSONObject.parseObject(redisJson, UserEntity.class);
JvmMapCacheUtils.putEntity(cacheKey, redisUserEntity);
return redisUserEntity;
}
// 3.如果二级缓存没有在查询数据库(三级缓存)
UserEntity dbUserEntity = userMapper.findByUser(id);
if (dbUserEntity == null) {
return null;
}
// 将数据存放二级和三级缓存
String cacheValue = JSONObject.toJSONString(dbUserEntity);
RedisUtil.setString(cacheKey, cacheValue);
JvmMapCacheUtils.putEntity(cacheKey, dbUserEntity);
return dbUserEntity;
}
}
装饰模式基本架构设计原理
在不改变原有代码的基础之上,新增附加功能
装饰模式应用场景
多级缓存设计、mybatis中一级与二级缓存、IO流/发送短信
对接短信接口 阿里云?—发送短信挂的情况下?
阿里云—最开始基本功能
腾讯云----额外增强
华为云—额外增强
装饰者模式定义
(1)抽象组件:定义一个抽象接口,来规范准备附加功能的类
(2)具体组件:将要被附加功能的类,实现抽象角色接口
(3)抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一致的接口
(4)具体装饰:实现抽象装饰者角色,负责对具体构件添加额外功能。
定义早期装饰模式一级缓存
package com.mayikt.decorate;
/**
* @author 余胜军
* @ClassName ComponentCache
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public interface ComponentCache {
<T> T getCache(String key);
}
package com.mayikt.decorate;
import com.mayikt.entity.UserEntity;
import com.mayikt.mapper.UserMapper;
import com.mayikt.utils.JvmMapCacheUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author 余胜军
* @ClassName JvmComponentCache
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
public class JvmComponentCache implements ComponentCache {
@Autowired
private UserMapper userMapper;
@Override
public <T> T getCache(String key) {
//1.先查询一级缓存
UserEntity jvmUser = JvmMapCacheUtils.getEntity(key, UserEntity.class);
if (jvmUser != null) {
return (T) jvmUser;
}
//2.查询db
UserEntity dbUser = userMapper.findByUser(1);
if (dbUser == null) {
return null;
}
JvmMapCacheUtils.putEntity(key, dbUser);
return (T) dbUser;
}
}
基于装饰模式重构设计多级缓存
基本功能
package com.mayikt.decorate;
/**
* @author 余胜军
* @ClassName ComponentCache
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public interface ComponentCache {
<T> T getCache(String key);
}
package com.mayikt.decorate;
import com.mayikt.entity.UserEntity;
import com.mayikt.mapper.UserMapper;
import com.mayikt.utils.JvmMapCacheUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author 余胜军
* @ClassName JvmComponentCache
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
public class JvmComponentCache implements ComponentCache {
@Autowired
private UserMapper userMapper;
@Override
public <T> T getCache(String key) {
//1.先查询一级缓存
UserEntity jvmUser = JvmMapCacheUtils.getEntity(key, UserEntity.class);
if (jvmUser == null) {
return null;
}
return (T) jvmUser;
}
}
装饰模式重构
这里查询db中的参数是暂时写死的,主要体现多级缓存实现思路。
package com.mayikt.decorate;
/**
* @author 余胜军
* @ClassName AbstractDecorate
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public interface AbstractDecorate extends ComponentCache {
}
package com.mayikt.decorate;
import com.alibaba.fastjson.JSONObject;
import com.mayikt.entity.UserEntity;
import com.mayikt.utils.JvmMapCacheUtils;
import com.mayikt.utils.RedisUtil;
import org.apache.catalina.User;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* @author 余胜军
* @ClassName RedisDecorate
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
public class RedisDecorate extends JvmComponentCache implements AbstractDecorate {
@Override
public <T> T getCache(String key) {
// 先查询一级缓存
UserEntity jvmUserEntity = super.getCache(key);
if (jvmUserEntity != null) {
return (T) jvmUserEntity;
}
// 一级缓存中没有该数据查询二级缓存
String redisJson = RedisUtil.getString(key);
if (StringUtils.isEmpty(redisJson)) {
return null;
}
UserEntity redisUserEntity = JSONObject.parseObject(redisJson, UserEntity.class);
JvmMapCacheUtils.putEntity(key, redisUserEntity);
return (T) redisUserEntity;
}
}
package com.mayikt.decorate;
import com.alibaba.fastjson.JSONObject;
import com.mayikt.entity.UserEntity;
import com.mayikt.mapper.UserMapper;
import com.mayikt.utils.JvmMapCacheUtils;
import com.mayikt.utils.RedisUtil;
import org.apache.catalina.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author 余胜军
* @ClassName MySQLDecorate
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
public class MySQLDecorate extends RedisDecorate implements AbstractDecorate {
@Autowired
private UserMapper userMapper;
@Override
public <T> T getCache(String key) {
UserEntity redisUserEntity = super.getCache(key);
if (redisUserEntity != null) {
return (T) redisUserEntity;
}
UserEntity dbUser = userMapper.findByUser(1);
if (dbUser == null) {
return null;
}
// 将db中数据写入二级和一级缓存中
RedisUtil.setString(key, JSONObject.toJSONString(dbUser));
JvmMapCacheUtils.putEntity(key, dbUser);
return (T) dbUser;
}
}