美文网首页
03.装饰模式设计多级缓存框架&外观模式

03.装饰模式设计多级缓存框架&外观模式

作者: 滔滔逐浪 | 来源:发表于2022-06-21 22:39 被阅读0次
    image.png

    jvm缓存(一级缓存)
    优点:查询速度非常快
    缺点:
    1,jvm内置缓存与业务申请内存没有解耦,有可能会发生内存溢出问题
    2,缓存容量比较少的
    3,jvm内置缓存集群的话要注意数据一致性的问题
    4,jvm内存缓存jvm服务重启,内存数据就没有了
    缓存---当缓存满了缓存淘汰策略将近期没有使用的缓存自动清理掉
    jvm内置缓存: EhCache,OSCache 底层就是一个map结合。
    触发gc回收时,stw短暂暂停用户线程,会形成短暂卡顿问题。
    将数据存放在内存---将对象序列化json
    从内存中读取数据到程序中(反序列化json数据变成对象)
    将数据存放在内存,redis,数据库。

    redis缓存(二级缓存)
    优点: 查询数据存放内存中,缓存数据非常多redis集群,。
    缺点: 数据直接持久化在硬盘中,查询效率偏低,会经过磁盘io


    image.png

    在实际开发项目,为了减少数据库的访问压力,我们都会将数据缓存到内存中,比如:Redis(分布式缓存)、EhCache(JVM内置缓存)。
    缓存级别越小缓存内容越少,缓存级别越大缓存内容越多;
    例如在早期项目中,项目比较小可能不会使用Redis作为缓存,使用JVM内置的缓存框架,项目比较大的时候开始采用Redis分布式缓存框架,这时候需要设计一级与二级缓存。
    缓存机制:jvm内置缓存: 将数据缓存到当前jvm中 缺陷:占用当前jvm内存空间,可能会造成内存溢出问题;集群很难保证各个节点之间数据同步问题。举例:EhCache,OsCache底层原理采用HashMap实现 淘汰策略
    分布式缓存Redis:数据能够共享
    装饰模式概念
    不改变原有的代码实现增强。mybatis,hibernate 二级缓存都属于开发者自己去实现扩展功能。
    装饰模式与代理模式区别
    代理模式对目标对象(目标方法)实现增强:
    装饰模式对装饰对象实现增强,不能改变原有代码。
    基于HashMap手写Jvm内置缓存

    public class JvmMapCacheUtils<V> {
        private  static HashMap<String, String> cacheMapping=new HashMap<>();
        public static void putEntity(String key,Object object){
            cacheMapping.put(key, JSONObject.toJSONString(object));
        }
    
        public static <T> T getEntity(String key,Class<T> t){
            String json=cacheMapping.get(key);
            JSONObject jsonObject=JSONObject.parseObject(json);
            return  JSONObject.parseObject(json,t);
        }
    
        public static void main(String[] args) {
            UserEntity userEntity=new UserEntity(1,"3222",22);
            JvmMapCacheUtils.putEntity("user1",userEntity);
            UserEntity userEntity1=JvmMapCacheUtils.getEntity("user1",UserEntity.class);
            System.out.println(userEntity1);
        }
    }
    

    模拟一级与二、三级缓存概念

    package com.taotao.entity;
    
    import lombok.Data;
    
    /**
     * @author wangjin
     * @title: UserEntity
     * @projectName designmodule
     * @description: TODO
     * @date 2022/6/19 0019 19:53
     */
    @Data
    public class UserEntity {
        private int id;
        private String name;
        private int age;
    
        public UserEntity() {
        }
    
        public UserEntity(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "UserEntity{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    package com.taotao.mapper;
    
    import com.taotao.entity.UserEntity;
    import org.apache.ibatis.annotations.Select;
    
    /**
     * @author wangjin
     * @title: UserMapper
     * @projectName designmodule
     * @description: TODO
     * @date 2022/6/19 0019 20:00
     */
    public interface UserMapper {
        @Select("select * from users where id=#{id} ")
        UserEntity findByUser(Integer id);
    }
    
    
    package com.taotao.utils;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * Redis工具类
     */
    @Component
    public class RedisUtil implements ApplicationContextAware {
    
        /**
         * 以后学会工具类封装
         * 直接通过类名称.方法
         * 在该类里面有些程序属性----需要 在ioc容器获取。
         * bean生命周期回调获取。
         */
        @Autowired
        private static StringRedisTemplate stringRedisTemplate;
        private static ApplicationContext applicationContext;
    
        /**
         * 存放string类型
         *
         * @param key     key
         * @param data    数据
         * @param timeout 超时间
         */
        public static void setString(String key, String data, Long timeout) {
            getStringRedisTemplate().opsForValue().set(key, data);
            if (timeout != null) {
                getStringRedisTemplate().expire(key, timeout, TimeUnit.SECONDS);
            }
        }
    
        /**
         * 存放string类型
         *
         * @param key  key
         * @param data 数据
         */
        public static void setString(String key, String data) {
            setString(key, data, null);
        }
    
        /**
         * 根据key查询string类型
         *
         * @param key
         * @return
         */
        public static String getString(String key) {
            String value = getStringRedisTemplate().opsForValue().get(key);
            return value;
        }
    
        /**
         * 根据对应的key删除key
         *
         * @param key
         */
        public static void delKey(String key) {
            getStringRedisTemplate().delete(key);
        }
    
    //    @Override
    //    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    //        this.applicationContext = applicationContext;
    //    }
    
        public static StringRedisTemplate getStringRedisTemplate() {
            if (stringRedisTemplate == null) {
                stringRedisTemplate = applicationContext.getBean("stringRedisTemplate", StringRedisTemplate.class);
            }
            return stringRedisTemplate;
        }
    
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    
    server:
      port: 60
      redis:
        host: 127.0.0.1
        port: 6379
    
    spring:
      ###数据库相关连接
      datasource:
        username: root
        password: root
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    ####打印MyBatias日志
    logging:
      level:
        ### 开发环境使用DEBUG 生产环境info或者error
        com.taotao.mapper: DEBUG
    
    
    package com.taotao;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * @author wangjin
     * @title: AppDecorate
     * @projectName designmodule
     * @description: TODO
     * @date 2022/6/19 0019 14:31
     */
    @MapperScan("com.taotao.mapper")
    @SpringBootApplication
    public class AppDecorate {
        public static void main(String[] args) {
           SpringApplication.run(AppDecorate.class,args);
        }
    }
    
    
    
    package com.taotao.service;
    
    import com.alibaba.fastjson.JSONObject;
    import com.taotao.entity.UserEntity;
    import com.taotao.mapper.UserMapper;
    import com.taotao.utils.JvmMapCacheUtils;
    import com.taotao.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 wangjin
     * @title: MemberService
     * @projectName designmodule
     * @description: TODO
     * @date 2022/6/19 0019 19:59
     */
    @RestController
    public class MemberService {
        @Autowired
        private UserMapper userMapper;
        @RequestMapping("/getMember")
        public UserEntity getMember(Integer id) {
            if (StringUtils.isEmpty(id)) {
                return null;
            }
            // 1.先查询一级缓存,一级缓存有的情况下 直接返回 如果一级缓存没有的情况下则 查询二级缓存
            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 redisUser = JSONObject.parseObject(redisJson, UserEntity.class);
                // 需要将数据存放在 jvm内存中
                JvmMapCacheUtils.putEntity(cacheKey, redisUser);
                return redisUser;
            }
            // 3.查询三级缓存 数据库
            UserEntity dbUser = userMapper.findByUser(id);
            if (dbUser == null) {
                return null;
            }
            // 三级缓存中数据存放在二级和一级缓存
            RedisUtil.setString(cacheKey, JSONObject.toJSONString(dbUser));
            JvmMapCacheUtils.putEntity(cacheKey, dbUser);
            // 扩展新增级别缓存 改动了
            return dbUser;
        }
    }
    
    

    装饰模式基本框架架构设计原理
    在不改变原有代码的情况下,新增附加功能
    装饰模式应用场景
    多级缓存设计,mybatis中一级与二级缓存,io流/发送短信


    image.png

    装饰者模式定义(1)抽象组件;定义一个抽象接口,来规范准备附加功能的类
    (2)具体组件:将要被附加功能的类,实现抽象角色接口
    (3): 抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一直的接口
    4.具体装饰:实现抽象装饰者角色,负责对具体构件添加额外功能。

    package com.taotao.decorate;
    
    /**
     * @Author: wangjin
     * @CreateTime: 2022-06-21  22:55
     */
    public interface ComponentCache {
        /**
         * 返回泛型T
         *
         * @param <T>
         * @return
         */
        <T> T getCache(String key,Integer id);
    }
    
    
    
    package com.taotao.decorate;
    
    /**
     * @Author: wangjin
     * @CreateTime: 2022-06-22  21:03
     */
    public interface AbstractDecorate  extends ComponentCache{
    }
    
    
    package com.taotao.decorate;
    
    import com.taotao.entity.UserEntity;
    import com.taotao.utils.JvmMapCacheUtils;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author: wangjin
     * @CreateTime: 2022-06-21  22:57
     */
    @Component
    public class JvmComponentCache  implements ComponentCache{
        @Override
        public <T> T getCache(String key,Integer id) {
            //1,一级缓存,从jvm中查询到数据
            UserEntity userEntity= JvmMapCacheUtils.getEntity(key,UserEntity.class);
            return (T) userEntity;
        }
    }
    
    
    
    package com.taotao.decorate;
    
    import com.alibaba.fastjson.JSONObject;
    import com.taotao.entity.UserEntity;
    import com.taotao.utils.JvmMapCacheUtils;
    import com.taotao.utils.RedisUtil;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    
    /**
     * @Author: wangjin
     * @CreateTime: 2022-06-22  21:04
     */
    @Component
    public class RedisDecorate extends JvmComponentCache implements AbstractDecorate {
        //二级装饰缓存,装饰不改变原来的代码实现增强
        @Override
        public <T> T getCache(String key,Integer id) {
            //先查询一级缓存,一级缓存有数据直接返回
            UserEntity jvmUser= super.getCache(key,id);
            if(jvmUser !=null){
                return (T)jvmUser;
            }
            //查询二级缓存,二级缓存如果有就返回
            // 2.查询二级缓存 (走redis)
            String redisJson = RedisUtil.getString(key);
            if (!StringUtils.isEmpty(redisJson)) {
                UserEntity redisUser = JSONObject.parseObject(redisJson, UserEntity.class);
                // 需要将数据存放在 jvm内存中
                JvmMapCacheUtils.putEntity(key, redisUser);
                return  (T)redisUser;
            }
    
            return null;
        }
    }
    
    
    package com.taotao.decorate;
    
    import com.alibaba.fastjson.JSONObject;
    import com.taotao.entity.UserEntity;
    import com.taotao.mapper.UserMapper;
    import com.taotao.utils.JvmMapCacheUtils;
    import com.taotao.utils.RedisUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author: wangjin
     * @CreateTime: 2022-06-22  21:14
     */
    @Component
    public class MySQLDecorate extends RedisDecorate  implements  AbstractDecorate{
        @Autowired
        private UserMapper userMapper;
        @Override
        public <T> T getCache(String key,Integer id) {
            UserEntity userEntity=super.getCache(key,id);
            if(userEntity!=null){
                return  (T)userEntity;
            }
            // 3.查询三级缓存 数据库
            UserEntity dbUser = userMapper.findByUser(id);
            if (dbUser == null) {
                return null;
            }
            // 三级缓存中数据存放在二级和一级缓存
            RedisUtil.setString(key, JSONObject.toJSONString(dbUser));
            JvmMapCacheUtils.putEntity(key, dbUser);
            // 扩展新增级别缓存 改动了
            return (T)dbUser;
        }
    }
    
    
    package com.taotao.service;
    
    import com.alibaba.fastjson.JSONObject;
    import com.taotao.decorate.MySQLDecorate;
    import com.taotao.entity.UserEntity;
    import com.taotao.mapper.UserMapper;
    import com.taotao.utils.JvmMapCacheUtils;
    import com.taotao.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 wangjin
     * @title: MemberService
     * @projectName designmodule
     * @description: TODO
     * @date 2022/6/19 0019 19:59
     */
    @RestController
    public class MemberService {
        @Autowired
        private UserMapper userMapper;
        @Autowired
        private MySQLDecorate mySQLDecorate;
        @RequestMapping("/getMember")
        public UserEntity getMember(Integer id) {
            if (StringUtils.isEmpty(id)) {
                return null;
            }
            // 1.先查询一级缓存,一级缓存有的情况下 直接返回 如果一级缓存没有的情况下则 查询二级缓存
            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 redisUser = JSONObject.parseObject(redisJson, UserEntity.class);
                // 需要将数据存放在 jvm内存中
                JvmMapCacheUtils.putEntity(cacheKey, redisUser);
                return redisUser;
            }
            // 3.查询三级缓存 数据库
            UserEntity dbUser = userMapper.findByUser(id);
            if (dbUser == null) {
                return null;
            }
            // 三级缓存中数据存放在二级和一级缓存
            RedisUtil.setString(cacheKey, JSONObject.toJSONString(dbUser));
            JvmMapCacheUtils.putEntity(cacheKey, dbUser);
            // 扩展新增级别缓存 改动了
            return dbUser;
        }
    
    
        @RequestMapping("/getUser")
        public  UserEntity getUser(Integer id){
            String cacheKey = "com.mayikt.service.MemberService.getMember,Integer:" + id;
            return mySQLDecorate.getCache(cacheKey,id);
        }
    
    }
    
    
    

    相关文章

      网友评论

          本文标题:03.装饰模式设计多级缓存框架&外观模式

          本文链接:https://www.haomeiwen.com/subject/hwjrvrtx.html