精华内容
下载资源
问答
  • 然后,其他项目,开启缓存,从redis15库获取到所有数据。放到本地的缓存。所以缓存这一块考虑使用LocalCache。 首先:domain项目,实现redis的多数据源配置。然后刷新缓存按钮触发时,把数据从DB存储到...

    在srping cloud中,有个domain项目为数据字典,我现在的需求是,数据字典放在redis的15中,所以需要配置redis的多数据源。然后,在其他项目中,开启缓存,从redis15库中获取到所有数据。放到本地的缓存。所以缓存这一块考虑使用LocalCache。

    首先:在domain项目中,实现redis的多数据源配置。然后在刷新缓存按钮触发时,把数据从DB存储到redis15库,在这个时候,需要去使用redis中的订阅发布来通知缓存去实时更新。

    配置文件中加入自定义redis的配置

    domainRedis:
       host: 192.168.7.162
       port: 30379
       password:
       # 连接超时时间(毫秒)
       timeout: 10000
       # 连接池中的最大空闲连接
       max-idle: 8
       # 连接池中的最小空闲连接
       min-idle: 10
       # 连接池最大连接数(使用负值表示没有限制)
       max-active: 100
       # 连接池最大阻塞等待时间(使用负值表示没有限制)
       max-wait: -1
       database: 15
    

    RedisConfig.java 连接自定义redis

    package com.bbg.domainManager.common.redis;
    
    import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
    import com.bbg.domainManager.common.ConstantConfiguration;
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisPassword;
    import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.listener.PatternTopic;
    import org.springframework.data.redis.listener.RedisMessageListenerContainer;
    import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import java.time.Duration;
    
    /**
     * redis配置类
     *redisTemplate 默认jdk序列方式,用来保存对象等
      *  stringRedisTemplate 默认string的序列化方式,用于存储string格式
     * @author zcc ON 2018/3/19
     **/
    @Configuration
    @EnableCaching//开启注解
    public class RedisConfig extends CachingConfigurerSupport {
    
    
        
        @Primary
        @Bean
        public RedisConnectionFactory RedisConnectionFactory() {
            /* ========= 基本配置 ========= */
            RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
            configuration.setHostName(hostName);
            configuration.setPort(port);
            configuration.setDatabase(database);
            if (!ObjectUtils.isEmpty(password)) {
                RedisPassword redisPassword = RedisPassword.of(password);
                configuration.setPassword(redisPassword);
            }
    
             /* ========= 连接池通用配置 ========= */
            GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
            genericObjectPoolConfig.setMaxTotal(maxActive);
            genericObjectPoolConfig.setMinIdle(minIdle);
            genericObjectPoolConfig.setMaxIdle(maxIdle);
            genericObjectPoolConfig.setMaxWaitMillis(maxWait);
    
            LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
            builder.poolConfig(genericObjectPoolConfig);
            builder.commandTimeout(Duration.ofSeconds(timeout));
            LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration, builder.build());
    
    
            return connectionFactory;
        }
    
        
         /**
          * 数据源2 redis template
          */
         @Bean(name = "domainRedis")
         public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
    //
    //        /* ========= 基本配置 ========= */
    //         RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
    //         configuration.setHostName(hostName);
    //         configuration.setPort(port);
    //         configuration.setDatabase(database);
    //         if (!ObjectUtils.isEmpty(password)) {
    //             RedisPassword redisPassword = RedisPassword.of(password);
    //             configuration.setPassword(redisPassword);
    //         }
    //
    //        /* ========= 连接池通用配置 ========= */
    //         GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
    //         genericObjectPoolConfig.setMaxTotal(maxActive);
    //         genericObjectPoolConfig.setMinIdle(minIdle);
    //         genericObjectPoolConfig.setMaxIdle(maxIdle);
    //         genericObjectPoolConfig.setMaxWaitMillis(maxWait);
    //
    //        /* ========= jedis pool ========= */
    //        /*
    //        JedisClientConfiguration.DefaultJedisClientConfigurationBuilder builder = (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder) JedisClientConfiguration
    //                .builder();
    //        builder.connectTimeout(Duration.ofSeconds(timeout));
    //        builder.usePooling();
    //        builder.poolConfig(genericObjectPoolConfig);
    //        JedisConnectionFactory connectionFactory = new JedisConnectionFactory(configuration, builder.build());
    //        // 连接池初始化
    //        connectionFactory.afterPropertiesSet();
    //        */
    //
    //        /* ========= lettuce pool ========= */
    //         LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
    //         builder.poolConfig(genericObjectPoolConfig);
    //         builder.commandTimeout(Duration.ofSeconds(timeout));
    //         LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration, builder.build());
    //         connectionFactory.afterPropertiesSet();
    
            /* ========= 创建 template ========= */
             return createRedisTemplate(connectionFactory);
         }
    
         /**
          * json 实现 redisTemplate
          * <p>
          * 该方法不能加 @Bean 否则不管如何调用,connectionFactory都会是默认配置
          *
          * @param redisConnectionFactory
          * @return
          */
         public RedisTemplate createRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
             RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
             redisTemplate.setConnectionFactory(redisConnectionFactory);
    
         //    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
             //调用自己写的StringJackson2JsonSerializer来序列化,value中存储的是json
             StringJackson2JsonSerializer<Object> jackson2JsonRedisSerializer = new StringJackson2JsonSerializer<Object>(Object.class);
    
             ObjectMapper objectMapper = new ObjectMapper();
             objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
             objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
             jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    
             RedisSerializer stringSerializer = new StringRedisSerializer();
    
             redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
             redisTemplate.setKeySerializer(stringSerializer);
    
             redisTemplate.setHashKeySerializer(stringSerializer);
             redisTemplate.setHashValueSerializer(stringSerializer);
    
    
             redisTemplate.afterPropertiesSet();
    
             return redisTemplate;
         }
    
    /*
    Redis 连接池: Lettuce、Jedis 比较
    
    Jedis:
    Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接
    
    Lettuce:
    Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
    lettuce主要利用netty实现与redis的同步和异步通信。
     */
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            RedisCacheManager cacheManager = RedisCacheManager.create(factory);
    
            return cacheManager;
        }
        // 以下两种redisTemplate自由根据场景选择
    //    @Bean
    //    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
    //        RedisTemplate<Object, Object> template = new RedisTemplate<>();
    //        template.setConnectionFactory(connectionFactory);
    //
    //        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
    //        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
    //
    //        ObjectMapper mapper = new ObjectMapper();
    //        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    //        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    //        serializer.setObjectMapper(mapper);
    //
    //        template.setValueSerializer(serializer);
    //        //使用StringRedisSerializer来序列化和反序列化redis的key值
    //        template.setHashKeySerializer(serializer);
    //        template.setHashValueSerializer(serializer);
    //        template.setKeySerializer(new StringRedisSerializer());
    //        template.afterPropertiesSet();
    //        return template;
    //    }
        @Bean
        public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
            StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
            stringRedisTemplate.setConnectionFactory(factory);
            return stringRedisTemplate;
        }
    
        /**
         * redis消息监听器容器
         * 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
         * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
         *
         * @param connectionFactory
         * @param listenerColumnDictAdapter
         * @param listenerSysCconfigAdapter
         * @return
         */
        @Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                                MessageListenerAdapter listenerColumnDictAdapter,
                                                MessageListenerAdapter listenerSysCconfigAdapter) {
    
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            // 订阅通道指标映射字典
            container.addMessageListener(listenerColumnDictAdapter, new PatternTopic(ConstantConfiguration.TOPIC_USERNAME));
            // 订阅系统配置频道
            container.addMessageListener(listenerSysCconfigAdapter, new PatternTopic(ConstantConfiguration.TOPIC_USERNAME));
            return container;
        }
    
        /**
         * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
         *
         * @param redisMessageReceiver
         * @return
         */
        @Bean
        MessageListenerAdapter listenerColumnDictAdapter(RedisMessageReceiver redisMessageReceiver) {
            return new MessageListenerAdapter(redisMessageReceiver, "receiveColumnDictMessage");
        }
    
        @Bean
        MessageListenerAdapter listenerSysCconfigAdapter(RedisMessageReceiver redisMessageReceiver) {
            return new MessageListenerAdapter(redisMessageReceiver, "receiveSysConfigMessage");
        }
    
        @Bean
        StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
            return new StringRedisTemplate(connectionFactory);
        }
    
    
    }
    

    RedisUtils.java redis的操作工具类

    package com.bbg.domainManager.common.redis;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/7/15 001516:58
     */
    
    import org.springframework.data.redis.core.*;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.io.Serializable;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    @Component
    public class RedisUtils {
    
    //    @Autowired
    //    private RedisTemplate redisTemplate;
    
        @Resource(name = "domainRedis")
        private RedisTemplate redisTemplate;
    
        public RedisUtils(RedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        /**
         * 写入缓存
         * @param key
         * @param value
         * @return
         */
        public boolean set(final String key, Object value) {
            boolean result = false;
            try {
                ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
                operations.set(key, value);
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        /**
         * 写入缓存设置时效时间
         * @param key
         * @param value
         * @return
         */
        public boolean set(final String key, Object value, Long expireTime) {
            boolean result = false;
            try {
                ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
                operations.set(key, value);
                redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        /**
         * 批量删除对应的value
         * @param keys
         */
        public void remove(final String... keys) {
            for (String key : keys) {
                remove(key);
            }
        }
    
        /**
         * 批量删除key
         * @param pattern
         */
        public void removePattern(final String pattern) {
            Set<Serializable> keys = redisTemplate.keys(pattern+"*");
            if (keys.size() > 0)
                redisTemplate.delete(keys);
        }
        /**
         * 删除对应的value
         * @param key
         */
        public void remove(final String key) {
            if (exists(key)) {
                redisTemplate.delete(key);
            }
        }
        /**
         * 判断缓存中是否有对应的value
         * @param key
         * @return
         */
        public boolean exists(final String key) {
            return redisTemplate.hasKey(key);
        }
        /**
         * 读取缓存
         * @param key
         * @return
         */
        public Object get(final String key) {
            Object result = null;
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            result = operations.get(key);
            return result;
        }
    
    
    
        /**
         * 获取domain对象
         */
        public static DomainDto getDomain(String key) throws IOException, ClassNotFoundException {
            DomainDto domainDto = null;
    
            domainDto = map != null &&  map.get(key) !=null  ? (DomainDto)map.get(key) : domainDto  ;
    
    
            return domainDto == null ? domainDto : (DomainDto)CopyUtils.deepClone(domainDto);//获取缓存之后,进行深度复制,防止修改
    //        return (DomainDto)CopyUtils.deepClone(map.get("sys_domain:"+key));//获取缓存之后,进行深度复制,防止修改
        }
    
        /**
         * 获取对应域值
         * key:域key
         * defineKey: 值key
         */
        public static DomainDefineDto getDomainDefine(String key,String defineKey ) throws IOException, ClassNotFoundException {
           DomainDto dto = getDomain(key);
    
            DomainDefineDto defineDto = null;
            if(dto != null ){
                defineDto = dto.getMap().get(defineKey);
            }
    
            return defineDto;
        }
    
        /**
         * 获取对应二级域值集合
         * key:域key
         * groupdmdkey: 二级分组值key
         */
        public static List<DomainDefineDto> getDomainDefineCollection(String key, String groupdmdkey ) throws IOException, ClassNotFoundException {
    
            DomainDto dto = getDomain(key);
            DomainDefineDto defineDto = null;
            if(dto != null ){
                defineDto = dto.getMap().get(groupdmdkey);
    
            }
    
            return defineDto.getGroupdmdList();
        }
    
    
     /**
         * 获取对应域动作
         * key:域key
         * transitionKey: 动作key
         */
        public static DomainTransitionDto getDomainTransition(String key, String transitionKey ) throws IOException, ClassNotFoundException {
            DomainDto dto = getDomain(key);
    
            DomainTransitionDto domainTransitionDto = null;
            if(dto != null ){
                domainTransitionDto = dto.getTrMap().get(transitionKey);
            }
    
            return domainTransitionDto;
        }
        /**
         
         * 哈希 添加
         * @param key
         * @param hashKey
         * @param value
         */
        public void hmSet(String key, Object hashKey, Object value){
            HashOperations<String, Object, Object>  hash = redisTemplate.opsForHash();
            hash.put(key,hashKey,value);
        }
    
        /**
         * 哈希获取数据
         * @param key
         * @param hashKey
         * @return
         */
        public Object hmGet(String key, Object hashKey){
            HashOperations<String, Object, Object>  hash = redisTemplate.opsForHash();
            return hash.get(key,hashKey);
        }
    
        /**
         * 列表添加
         * @param k
         * @param v
         */
        public void lPush(String k,Object v){
            ListOperations<String, Object> list = redisTemplate.opsForList();
            list.rightPush(k,v);
        }
    
        /**
         * 列表获取
         * @param k
         * @param l
         * @param l1
         * @return
         */
        public List<Object> lRange(String k, long l, long l1){
            ListOperations<String, Object> list = redisTemplate.opsForList();
            return list.range(k,l,l1);
        }
    
        /**
         * 集合添加
         * @param key
         * @param value
         */
        public void add(String key,Object value){
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            set.add(key,value);
        }
    
        /**
         * 集合获取
         * @param key
         * @return
         */
        public Set<Object> setMembers(String key){
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            return set.members(key);
        }
    
        /**
         * 有序集合添加
         * @param key
         * @param value
         * @param scoure
         */
        public void zAdd(String key,Object value,double scoure){
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            zset.add(key,value,scoure);
        }
    
        /**
         * 有序集合获取
         * @param key
         * @param scoure
         * @param scoure1
         * @return
         */
        public Set<Object> rangeByScore(String key,double scoure,double scoure1){
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.rangeByScore(key, scoure, scoure1);
        }
    
    
        /**
         * 获取所有的key-value
         */
        public HashMap<Object,Object> getAll(){
            //获取所有的key
            Set<String> keys = redisTemplate.keys("*");
    
            //创建集合
            HashMap<Object,Object> map = new HashMap<>();
    
            //循环
            Object result ;
            for(String key : keys){
                result = null;
                ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
                result = operations.get(key);
    
                map.put(key,result);
            }
            return map;
        }
    }
    

    RedisMessageReceiver.java 处理订阅消息的类这个时候其实自己是发布者,不需要实现订阅的逻辑

    package com.bbg.domainManager.common.redis;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/11/22 002214:22
     */
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    /**
     *  处理订阅消息的类
     * @author xwq
     * @create 2019-11-22 14:22
     **/
    @Component
    public class RedisMessageReceiver {
    
        private static Logger logger = LoggerFactory.getLogger(RedisMessageReceiver.class);
    
        /**
         * 接受消息后,执行的方法
         * @param message
         */
        public void receiveColumnDictMessage(String message) {
            logger.info("redis订阅者接受消息:{},更新全局变量缓存dict_column", message);
    //        ApplicationStartedInitializer bean = BeanUtil.getBean(ApplicationStartedInitializer.class);
    //        bean.initColumnDict();
        }
    
        /**
         * redis 系统配置监听器
         * @param message
         */
        public void receiveSysConfigMessage(String message) {
            logger.info("redis订阅者接受消息:{},更新全局变量缓存sysConfig", message);
    //        ApplicationStartedInitializer bean = BeanUtil.getBean(ApplicationStartedInitializer.class);
    //        bean.initSysConfig();
        }
    }
    
    

    StringJackson2JsonSerializer.java 自定义的序列化,redis中value存储的值序列化为json

    package com.bbg.domainManager.common.redis;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/11/13 001310:28
     */
    
    import com.alibaba.fastjson.JSON;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.SerializationException;
    
    /**
     *
     * @author xwq
     * @create 2019-11-13 10:28
     **/
    public class StringJackson2JsonSerializer<T> extends Jackson2JsonRedisSerializer<T> {
        private ObjectMapper objectMapper = new ObjectMapper();
    
        public StringJackson2JsonSerializer(Class<T> type) {
            super(type);
            // TODO Auto-generated constructor stub
        }
    
        public byte[] serialize(Object t) throws SerializationException {
    
            if (t == null) {
                return  new byte[0];
            }
            try {
                //将对象转为Json String然后再序列化,方便跨服务 JSON.toJSONString(
                return this.objectMapper.writeValueAsBytes(JSON.toJSONString(t));
    //            return this.objectMapper.writeValueAsBytes(JacksonUtil.objToJson(t));
            } catch (Exception ex) {
                throw new SerializationException("Could not write JSON: " + ex.getMessage(), ex);
            }
        }
    }
    

    ConstantConfiguration。java 自定义枚举

    package com.cloud.common.domain;
    
    /**
     * @ClassName ConstantConfiguration
     * @Description TODO
     * @Author wang
     * @Date 2019/7/26 16:30
     * @Version 1.0.1
     **/
    public class ConstantConfiguration {
    
        public static final String MYSQL = "com.mysql.jdbc.Driver";
        public static final String ORACLE = "oracle.jdbc.driver.OracleDriver";
    
        public static final String TOPIC_USERNAME = "TOPIC_USERNAME";
        public static final String DOMAIN = "DOMAIN";
        public static final String DOMAIN_DEFINE = "DOMAIN_DEFINE";
        public static final String DOMAIN_TRANSITION = "DOMAIN_TRANSITION";
    }
    

    页面中的刷新缓存按钮方法

    defineDto1 = new DomainDefineDto();
    				defineDto1.setStatus(DomainStatusEnum.EFFECTIVITY.getCode());
    				defineDto1.setDomainKey(father.getKey());
    				fatherList = domainDefineDao.findForList(defineDto1);
    
    			}
    
    
    			for(DomainDefineDto dto1 : listDomainDefine){
    				//根据域值的key,去查询出对应的动作
    //				DomainTransitionDto transitionDto = new DomainTransitionDto();
    //				transitionDto.setStatus(DomainStatusEnum.EFFECTIVITY.getCode());
    //				transitionDto.setSrcstatekey(dto1.getKey());
    //				transitionDto.setDomainKey(dto.getKey());
    //				List<DomainTransitionDto> listDomainTransition = domainTransitionDao.findForList(transitionDto);
    
    				List<DomainTransitionDto> dtList = dtdMap.get(dto.getKey());
    				//再挑选出起点为dto1.getKey(),的动作
    				if(dtList != null){
    					List<DomainTransitionDto> listDomainTransition = dtList.stream()
    							.filter((DomainTransitionDto dt) -> dtList.contains(dto1.getKey().equals(dt.getSrcstatekey()) ))
    							.collect(Collectors.toList());
    
    					for(DomainTransitionDto dtDto : listDomainTransition){
    						//去获取到起始点的名称
    						if(StringUtils.isNotBlank(mapDefine.get(dtDto.getSrcstatekey()) )) {
    							dtDto.setSrcstatekeyName(mapDefine.get(dtDto.getSrcstatekey()));
    						}
    						//去获取到终点的名称
    						if(StringUtils.isNotBlank(mapDefine.get(dtDto.getTgtstatekey()) )) {
    							dtDto.setTgtstatekeyName(mapDefine.get(dtDto.getTgtstatekey()));
    						}
    
    						dto1.setTransitionList(listDomainTransition);
    
    					}
    				}
    
    				//因为在域值中有个二级分组,这里需要处理
    				if(fatherList.size()>0){
    					List<DomainDefineDto> list2 = new ArrayList<>();
    					for(DomainDefineDto fatherDto :fatherList){
    						if(dto1.getKey().equals(fatherDto.getGroupdmdkey())){
    							fatherDto.setGroupdmdkeyName(dto1.getName());
    							list2.add(fatherDto);
    						}
    					}
    
    					dto1.setGroupdmdList(list2);
    
    					hashMap.put(dto1.getKey(),dto1);
    				}
    //				if(!StringUtils.isNotBlank(dto1.getGroupdmdkey())){
    //					//说明二级分组字段为空,那就是第一级
    //					List<DomainDefineDto> list2 = new ArrayList<>();
    //					for(DomainDefineDto dto2 : listDomainDefine){
    //						if(dto1.getKey().equals(dto2.getGroupdmdkey())){
    //							dto2.setGroupdmdkeyName(mapDefine.get(dto2.getGroupdmdkey()));
    //
    //							list2.add(dto2);
    //						}
    //					}
    //
    //					dto1.setGroupdmdList(list2);
    //
    //					hashMap.put(dto1.getKey(),dto1);
    //				}
    
    			}
    
    
    			dto.setMap((HashMap<String, DomainDefineDto>) hashMap);
    
    			//去查询域定义的所有的动作
    //			DomainTransitionDto t = new DomainTransitionDto();
    //			t.setStatus(DomainStatusEnum.EFFECTIVITY.getCode());
    //			t.setDomainKey(dto.getKey());
    //			List<DomainTransitionDto> transitionDtoList1= domainTransitionDao.findForList(t);
    
    			List<DomainTransitionDto> transitionDtoList1 = dtdMap.get(dto.getKey()) != null ? dtdMap.get(dto.getKey()) : new ArrayList<>();
    
    			Map<String, DomainTransitionDto> dtMap = new HashMap();
    			for(DomainTransitionDto dt : transitionDtoList1){
    				//去获取到起始点的名称
    				if(StringUtils.isNotBlank(mapDefine.get(dt.getSrcstatekey()) )) {
    					dt.setSrcstatekeyName(mapDefine.get(dt.getSrcstatekey()));
    				}
    				//去获取到终点的名称
    				if(StringUtils.isNotBlank(mapDefine.get(dt.getTgtstatekey()) )) {
    					dt.setTgtstatekeyName(mapDefine.get(dt.getTgtstatekey()));
    				}
    
    				dtMap.put(dt.getActionkeyname(),dt);
    			}
    
    			dto.setTrMap((HashMap<String, DomainTransitionDto>) dtMap);
    
    
    			mapdomain.put(dto.getKey(), dto);
    
    		}
    
    		//删除以前的缓存
    		redisUtils.set("sys_domain_map",mapdomain);
    
    		//在更新缓存之后,需要去通知
    		stringRedisTemplate.convertAndSend(ConstantConfiguration.TOPIC_USERNAME, "update column dict action");
    
    	}
    
    

    stringRedisTemplate.convertAndSend(ConstantConfiguration.TOPIC_USERNAME, “update column dict action”);就是发布

    到此时,redis的数据和数据库中已经同步了。接下来就是使用LocalCache来做本地缓存。之所以没有用redis的缓存自己去想。

    先说放在cloud-common公共项目中的代码,cloud-common项目没有main方法,没有配置文件,直接就打包成jar到maven私库,供其他项目使用,其实就是把每个项目里面有的代码抽取出来.

    在这里插入图片描述
    cache包为LocalCache的本地缓存操作类
    redis包为redis的操作类,基本和上面代码差不多,细微区别自己想要注意
    cache中的LocalCache.java

    package com.cloud.common.domain.cache;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/11/25 002510:46
     */
    
    
    import org.springframework.stereotype.Component;
    
    import java.io.Serializable;
    import java.util.Map;
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
    
    /**
     *  很多场景下,有些请求的数据,是不会经常改变的,这种时候,
     *  为了减少数据库的查询压力,可以将这一部分数据放入缓存中,
     *  直接从缓存中读取。除了一些像Redis等缓存外,还可以通过本地内存,作为缓存。
     *  下边将使用ConcurrentHashMap来实现本地缓存。
     *
     *
     ConcurrentHashMap --数据存储,线程安全的map
     ScheduledExecutorService --线程定时调度服务
     TimerTask --定时任务
     lambda表达式
    
    
     * @author xwq
     * @create 2019-11-25 10:46
     **/
    @Component
    public class LocalCache implements Serializable {
    
        /**
         * 1.用线程安全的ConcurrentHashMap来作为缓存数据的存储,
         *2.然后通过定时调度任务TimerTask,来实现控制缓存的有效时间,根据缓存设置的
         *超时时间,来定时清除对应的 key,实现缓存过期
         *3.实现一些静态方法,来增加缓存、获取缓存等
         */
    
        /**
         * 1.多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获
         *抛出的异常其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题
         *2.Timer内部是一个线程,任务1所需的时间超过了两个任务间的间隔时会导致问题
         *3.Timer执行周期任务时依赖系统时间
    
         */
        private static final Map<String, Object> map;
        private static final ScheduledExecutorService timerService;  //定时器
    
        /**
         * 默认有效时长
         */
        private static final long DEFAULT_TIMEOUT = 36;//默认过期时间
        private static final long SECOND_TIME = 1000;//默认过期时间
    
        /**
         * 初始化块总是在构造器之前执行
         * 静态初始化块执行的优先级高于非静态初始化块,在对象装载到JVM中时执行一次,仅能初始化类成员变量,即static修饰的数据成员
         * 静态初始化块是类相关的,系统将在类加载时执行静态初始化块,而不是在创建对象时才执行,因此静态初始化块总是比非静态初始化块先执行
         */
        static {
            map = new LRUMap<>();
            timerService = new ScheduledThreadPoolExecutor(1, new LocalCache.DaemonThreadFactory());
        }
    
        /**
         * 工具类
         */
        private LocalCache() {
        }
    
        static class LRUMap<K, V> extends ConcurrentHashMap<K, V> {
            /**
             * 默认缓存大小
             */
            private static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
    
            /**
             * 默认最大缓存大小
             */
            private static final int DEFAULT_MAX_CAPACITY = 1 << 30;
    
            /**
             * 默认加载因子
             */
            private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
            /**
             * 读写锁
             */
            private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
            private final Lock rLock = readWriteLock.readLock();
            private final Lock wLock = readWriteLock.writeLock();
    
    
            public LRUMap() {
                super(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
            }
    
            public LRUMap(int initialCapacity) {
                super(initialCapacity, DEFAULT_LOAD_FACTOR);
            }
    
            /**
             * 需要重写LinkedHashMap中removeEldestEntry方法;
             * 新增元素的时候,会判断当前map大小是否超过DEFAULT_MAX_CAPACITY,超过则移除map中最老的节点;
             * @param eldest
             * @return
             */
            protected boolean removeEldestEntry(Entry<K,V> eldest) {
                return size() > DEFAULT_MAX_CAPACITY;
            }
    
            public V put(K k, V v) {
                wLock.lock();
                try {
                    return super.put(k, v);
                } finally {
                    wLock.unlock();
                }
            }
    
            public V get(String k) {
                rLock.lock();
                try {
                    return super.get(k);
                } finally {
                    rLock.unlock();
                }
            }
    
            public void putAll(Map<? extends K, ? extends V> m) {
                wLock.lock();
                try {
                    super.putAll(m);
                } finally {
                    wLock.unlock();
                }
            }
    
            public V remove(Object k) {
                wLock.lock();
                try {
                    return super.remove(k);
                } finally {
                    wLock.unlock();
                }
            }
    
    
            public boolean containKey(K k) {
                rLock.lock();
                try {
                    return super.containsKey(k);
                } finally {
                    rLock.unlock();
                }
            }
    
            public int size() {
                rLock.lock();
                try {
                    return super.size();
                } finally {
                    rLock.unlock();
                }
            }
    
    
            public void clear() {
                wLock.lock();
                try {
                    super.clear();
                } finally {
                    wLock.unlock();
                }
            }
        }
    
        /**
         * 清除缓存的任务类
         */
        static class CleanWorkerTask implements Runnable {
            private String key;
    
            public CleanWorkerTask(String key) {
                this.key = key;
            }
    
            @Override
            public void run() {
                LocalCache.remove(key);
            }
        }
    
        private static final class DaemonThreadFactory implements ThreadFactory {
            private AtomicInteger atomicInteger = new AtomicInteger(0);
    
            @Override
            public Thread newThread(Runnable runnable) {
                Thread thread = new Thread(runnable);
                thread.setName("schedule-pool-Thread-" + atomicInteger.getAndIncrement());
                thread.setDaemon(true);
                return null;
            }
        }
    
    
        /**
         * 增加缓存
         */
        public static void add(String key, Object value) {
            map.put(key, value);
            timerService.schedule(new CleanWorkerTask(key), DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
        }
    
        /**
         * 增加缓存
         * @param timeout  有效时长
         */
        public static void add(String key, Object value, int timeout) {
            map.put(key, value);
            timerService.schedule(new CleanWorkerTask(key), timeout * SECOND_TIME, TimeUnit.MILLISECONDS);
        }
    
        public static void putAll(Map<String, Object> m, int timeout) {
            map.putAll(m);
            for (String key : m.keySet()) {
                timerService.schedule(new CleanWorkerTask(key), timeout * SECOND_TIME, TimeUnit.MILLISECONDS);
            }
        }
    
        public static void putAll(Map<String, Object> m) {
            map.putAll(m);
            for (String key : m.keySet()) {
                timerService.schedule(new CleanWorkerTask(key),  DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
            }
        }
    
    
        /**
         * 获取缓存
         */
        public static Object get(String key) {
            return map.get(key);
        }
    
        public static boolean containsKey(String key) {
            return map.containsKey(key);
        }
    
        /**
         *
         * @param key
         */
        public static void remove(String key) {
            map.remove(key);
        }
    
        /**
         *
         * @param
         */
        public static void clear() {
            map.clear();
        }
    
        public static int size() {
            return map.size();
        }
    
    
    
    }
    

    MyStartupRunner.java

    package com.cloud.common.domain.cache;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/11/25 002515:59
     */
    
    import com.cloud.common.domain.redis.RedisUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    
    import java.util.Map;
    
    /**
     *
     SpringBoot项目启动时初始化缓存资源
     Springboot中CommandLineRunner接口的
     Component 会在所有 Spring Beans都初始化之后SpringApplication.run()之前执行
     * @author xwq
     * @create 2019-11-25 15:59
     **/
    @Component
    public class MyStartupRunner implements CommandLineRunner {
        @Autowired
        private RedisUtils redisUtils;
    
        @Override
        public void run(String... args) throws Exception {
            //清除所有缓存
            LocalCache.clear();
    //        LocalCache.remove("*");
    
            //获取到所有的redis数据
            Map map =  redisUtils.getAll();
    
    //        LocalCache.putAll(map,-1);//存储进去缓存
            LocalCache.putAll(map);//存储进去缓存
        }
    }
    

    ConstantConfiguration.java 枚举还是和上面的一样

    package com.cloud.common.domain;
    
    /**
     * @ClassName ConstantConfiguration
     * @Description TODO
     * @Author wang
     * @Date 2019/7/26 16:30
     * @Version 1.0.1
     **/
    public class ConstantConfiguration {
    
        public static final String MYSQL = "com.mysql.jdbc.Driver";
        public static final String ORACLE = "oracle.jdbc.driver.OracleDriver";
    
        public static final String TOPIC_USERNAME = "TOPIC_USERNAME";
        public static final String DOMAIN = "DOMAIN";
        public static final String DOMAIN_DEFINE = "DOMAIN_DEFINE";
        public static final String DOMAIN_TRANSITION = "DOMAIN_TRANSITION";
    }
    
    
    

    redis包中的代码
    RedisConfig.java

    package com.cloud.common.domain.redis;
    
    import com.cloud.common.domain.ConstantConfiguration;
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisPassword;
    import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.data.redis.listener.PatternTopic;
    import org.springframework.data.redis.listener.RedisMessageListenerContainer;
    import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.util.ObjectUtils;
    
    import java.time.Duration;
    
    /**
     * redis配置类
     *redisTemplate 默认jdk序列方式,用来保存对象等
      *  stringRedisTemplate 默认string的序列化方式,用于存储string格式
     * @author zcc ON 2018/3/19
     **/
    @Configuration
    @EnableCaching//开启注解
    public class RedisConfig extends CachingConfigurerSupport {
    
    
         /**
          * 数据源2 redis template
          */
         @Bean(name = "domainRedis")
         public RedisTemplate redisTemplate(
             @Value("${spring.domainRedis.database}") int database,
             @Value("${spring.domainRedis.timeout}") long timeout,
             @Value("${spring.domainRedis.max-active}") int maxActive,
             @Value("${spring.domainRedis.max-wait}") int maxWait,
             @Value("${spring.domainRedis.max-idle}") int maxIdle,
             @Value("${spring.domainRedis.min-idle}") int minIdle,
    
             @Value("${spring.domainRedis.host}") String hostName,
             @Value("${spring.domainRedis.port}") int port,
             @Value("${spring.domainRedis.password}") String password) {
    
            /* ========= 基本配置 ========= */
             RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
             configuration.setHostName(hostName);
             configuration.setPort(port);
             configuration.setDatabase(database);
             if (!ObjectUtils.isEmpty(password)) {
                 RedisPassword redisPassword = RedisPassword.of(password);
                 configuration.setPassword(redisPassword);
             }
    
            /* ========= 连接池通用配置 ========= */
             GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
             genericObjectPoolConfig.setMaxTotal(maxActive);
             genericObjectPoolConfig.setMinIdle(minIdle);
             genericObjectPoolConfig.setMaxIdle(maxIdle);
             genericObjectPoolConfig.setMaxWaitMillis(maxWait);
    
            /* ========= jedis pool ========= */
            /*
            JedisClientConfiguration.DefaultJedisClientConfigurationBuilder builder = (JedisClientConfiguration.DefaultJedisClientConfigurationBuilder) JedisClientConfiguration
                    .builder();
            builder.connectTimeout(Duration.ofSeconds(timeout));
            builder.usePooling();
            builder.poolConfig(genericObjectPoolConfig);
            JedisConnectionFactory connectionFactory = new JedisConnectionFactory(configuration, builder.build());
            // 连接池初始化
            connectionFactory.afterPropertiesSet();
            */
    
            /* ========= lettuce pool ========= */
             LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
             builder.poolConfig(genericObjectPoolConfig);
             builder.commandTimeout(Duration.ofSeconds(timeout));
             LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration, builder.build());
             connectionFactory.afterPropertiesSet();
    
            /* ========= 创建 template ========= */
             return createRedisTemplate(connectionFactory);
         }
    
         /**
          * json 实现 redisTemplate
          * <p>
          * 该方法不能加 @Bean 否则不管如何调用,connectionFactory都会是默认配置
          *
          * @param redisConnectionFactory
          * @return
          */
         public RedisTemplate createRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
             RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
             redisTemplate.setConnectionFactory(redisConnectionFactory);
    
    //         Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
             StringJackson2JsonSerializer<Object> jackson2JsonRedisSerializer = new StringJackson2JsonSerializer<Object>(Object.class);
    
             ObjectMapper objectMapper = new ObjectMapper();
             objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
             objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
             jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    
             redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
             redisTemplate.setKeySerializer(new StringRedisSerializer());
             redisTemplate.afterPropertiesSet();
             return redisTemplate;
         }
    
    /*
    Redis 连接池: Lettuce、Jedis 比较
    
    Jedis:
    Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接
    
    Lettuce:
    Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
    lettuce主要利用netty实现与redis的同步和异步通信。
     */
    
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            RedisCacheManager cacheManager = RedisCacheManager.create(factory);
            return cacheManager;
        }
        // 以下两种redisTemplate自由根据场景选择
    //    @Bean
    //    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
    //        RedisTemplate<Object, Object> template = new RedisTemplate<>();
    //        template.setConnectionFactory(connectionFactory);
    //
    //        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
    //        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
    //
    //
    //        ObjectMapper mapper = new ObjectMapper();
    //        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    //        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    //        serializer.setObjectMapper(mapper);
    //
    //        template.setValueSerializer(serializer);
    //        //使用StringRedisSerializer来序列化和反序列化redis的key值
    //        template.setHashKeySerializer(serializer);
    //        template.setHashValueSerializer(serializer);
    //        template.setKeySerializer(new StringRedisSerializer());
    //        template.afterPropertiesSet();
    //        return template;
    //    }
    
    
    //    @Primary//指定为首要的缓存
    //    @Bean("cacheManager")
    //    public CacheManager redisCacheManager(RedisTemplate redisTemplate) {
    //        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60));
    //        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
    //        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    //    }
    
    
        @Bean
        public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
            StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
            stringRedisTemplate.setConnectionFactory(factory);
            return stringRedisTemplate;
        }
    
    
    
        /**
         * redis消息监听器容器
         * 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
         * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
         *
         * @param connectionFactory
         * @param listenerColumnDictAdapter
         * @param listenerSysCconfigAdapter
         * @return
         */
        @Bean
        RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                                MessageListenerAdapter listenerColumnDictAdapter,
                                                MessageListenerAdapter listenerSysCconfigAdapter) {
    
            RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(connectionFactory);
            // 订阅通道指标映射字典
            container.addMessageListener(listenerColumnDictAdapter, new PatternTopic(ConstantConfiguration.TOPIC_USERNAME));
            // 订阅系统配置频道
            container.addMessageListener(listenerSysCconfigAdapter, new PatternTopic(ConstantConfiguration.TOPIC_USERNAME));
            return container;
        }
    
        /**
         * 消息监听器适配器,绑定消息处理器,利用反射技术调用消息处理器的业务方法
         *
         * @param redisMessageReceiver
         * @return
         */
        @Bean
        MessageListenerAdapter listenerColumnDictAdapter(RedisMessageReceiver redisMessageReceiver) {
            return new MessageListenerAdapter(redisMessageReceiver, "receiveColumnDictMessage");
        }
    
        @Bean
        MessageListenerAdapter listenerSysCconfigAdapter(RedisMessageReceiver redisMessageReceiver) {
            return new MessageListenerAdapter(redisMessageReceiver, "receiveSysConfigMessage");
        }
    
        @Bean
        StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
            return new StringRedisTemplate(connectionFactory);
        }
    }
    
    
    

    RedisMessageReceiver.java

    package com.cloud.common.domain.redis;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/11/22 002214:22
     */
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import com.cloud.common.domain.cache.LocalCache;
    
    import java.util.Map;
    
    /**
     *  处理订阅消息的类
     * @author xwq
     * @create 2019-11-22 14:22
     **/
    @Component
    public class RedisMessageReceiver {
    
        private static Logger logger = LoggerFactory.getLogger(RedisMessageReceiver.class);
    
        @Autowired
        private RedisUtils redisUtils;
    
    
    
        /**
         * 接受消息后,执行的方法
         * @param message
         */
        public void receiveColumnDictMessage(String message) {
            logger.info("redis订阅者接受消息:{},更新全局变量缓存dict_column", message);
    
            //清除所有缓存
            LocalCache.clear();
    //        LocalCache.remove("*");
    
            //获取到所有的redis数据
            Map map =  redisUtils.getAll();
    
            LocalCache.putAll(map,-1);//存储进去缓存
        }
    
        /**
         * redis 系统配置监听器
         * @param message
         */
        public void receiveSysConfigMessage(String message) {
            logger.info("redis订阅者接受消息:{},系统配置监听器更新全局变量缓存sysConfig", message);
            //清除所有缓存
            LocalCache.clear();
    //        LocalCache.remove("*");
    
            //获取到所有的redis数据
            Map map =  redisUtils.getAll();
    
            LocalCache.putAll(map,-1);//存储进去缓存
        }
    }
    

    RedisUtils.java

    package com.cloud.common.domain.redis;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/7/15 001516:58
     */
    
    import org.springframework.data.redis.core.*;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.io.Serializable;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    @Component
    public class RedisUtils {
    
    //    @Autowired
    //    private RedisTemplate redisTemplate;
    
        @Resource(name = "domainRedis")
        private RedisTemplate redisTemplate;
    
        public RedisUtils(RedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        /**
         * 写入缓存
         * @param key
         * @param value
         * @return
         */
        public boolean set(final String key, Object value) {
            boolean result = false;
            try {
                ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
                operations.set(key, value);
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        /**
         * 写入缓存设置时效时间
         * @param key
         * @param value
         * @return
         */
        public boolean set(final String key, Object value, Long expireTime) {
            boolean result = false;
            try {
                ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
                operations.set(key, value);
                redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        /**
         * 批量删除对应的value
         * @param keys
         */
        public void remove(final String... keys) {
            for (String key : keys) {
                remove(key);
            }
        }
    
        /**
         * 批量删除key
         * @param pattern
         */
        public void removePattern(final String pattern) {
            Set<Serializable> keys = redisTemplate.keys(pattern+"*");
            if (keys.size() > 0)
                redisTemplate.delete(keys);
        }
        /**
         * 删除对应的value
         * @param key
         */
        public void remove(final String key) {
            if (exists(key)) {
                redisTemplate.delete(key);
            }
        }
        /**
         * 判断缓存中是否有对应的value
         * @param key
         * @return
         */
        public boolean exists(final String key) {
            return redisTemplate.hasKey(key);
        }
        /**
         * 读取缓存
         * @param key
         * @return
         */
        public Object get(final String key) {
            Object result = null;
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            result = operations.get(key);
            return result;
        }
        /**
         * 哈希 添加
         * @param key
         * @param hashKey
         * @param value
         */
        public void hmSet(String key, Object hashKey, Object value){
            HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
            hash.put(key,hashKey,value);
        }
    
        /**
         * 哈希获取数据
         * @param key
         * @param hashKey
         * @return
         */
        public Object hmGet(String key, Object hashKey){
            HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
            return hash.get(key,hashKey);
        }
    
        /**
         * 列表添加
         * @param k
         * @param v
         */
        public void lPush(String k,Object v){
            ListOperations<String, Object> list = redisTemplate.opsForList();
            list.rightPush(k,v);
        }
    
        /**
         * 列表获取
         * @param k
         * @param l
         * @param l1
         * @return
         */
        public List<Object> lRange(String k, long l, long l1){
            ListOperations<String, Object> list = redisTemplate.opsForList();
            return list.range(k,l,l1);
        }
    
        /**
         * 集合添加
         * @param key
         * @param value
         */
        public void add(String key,Object value){
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            set.add(key,value);
        }
    
        /**
         * 集合获取
         * @param key
         * @return
         */
        public Set<Object> setMembers(String key){
            SetOperations<String, Object> set = redisTemplate.opsForSet();
            return set.members(key);
        }
    
        /**
         * 有序集合添加
         * @param key
         * @param value
         * @param scoure
         */
        public void zAdd(String key,Object value,double scoure){
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            zset.add(key,value,scoure);
        }
    
        /**
         * 有序集合获取
         * @param key
         * @param scoure
         * @param scoure1
         * @return
         */
        public Set<Object> rangeByScore(String key,double scoure,double scoure1){
            ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
            return zset.rangeByScore(key, scoure, scoure1);
        }
    
        /**
         * 获取所有的key-value
         */
        public HashMap<Object,Object> getAll(){
            //获取所有的key
            Set<String> keys = redisTemplate.keys("*");
    
            //创建集合
            HashMap<Object,Object> map = new HashMap<>();
    
            //循环
            Object result ;
            for(String key : keys){
                result = null;
                ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
                result = operations.get(key);
    
                map.put(key,result);
            }
            return map;
        }
    
    }
    
    

    StringJackson2JsonSerializer.java

    package com.cloud.common.domain.redis;    /**
     * @Title: ${file_name}
     * @Package ${package_name}
     * @Description: ${todo}
     * @author xwq
     * @date 2019/11/13 001310:28
     */
    
    import com.alibaba.fastjson.JSON;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.SerializationException;
    
    /**
     *
     * @author xwq
     * @create 2019-11-13 10:28
     **/
    public class StringJackson2JsonSerializer<T> extends Jackson2JsonRedisSerializer<T> {
        private ObjectMapper objectMapper = new ObjectMapper();
    
        public StringJackson2JsonSerializer(Class<T> type) {
            super(type);
            // TODO Auto-generated constructor stub
        }
    
        public byte[] serialize(Object t) throws SerializationException {
    
            if (t == null) {
                return  new byte[0];
            }
            try {
                //将对象转为Json String然后再序列化,方便跨服务 JSON.toJSONString(
                return this.objectMapper.writeValueAsBytes(JSON.toJSONString(t));
    //            return this.objectMapper.writeValueAsBytes(JacksonUtil.objToJson(t));
            } catch (Exception ex) {
                throw new SerializationException("Could not write JSON: " + ex.getMessage(), ex);
            }
        }
    }
    

    cloud-common项目中的代码就完成了,接下来打包成jar到maven私库。
    然后我自己新建一个项目cloud-domain来测试代码。

    首先在pom中引入jar

    <dependency>
    			<groupId>com.bbg</groupId>
    			<artifactId>cloud-common</artifactId>
    			<version>0.0.1</version>
    			<scope>compile</scope>
    		</dependency>
    

    在配置文件中一定要加入自定义redis的配置

      domainRedis:
        host: 192.168.7.162
        port: 30379
        password:
        # 连接超时时间(毫秒)
        timeout: 10000
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池中的最小空闲连接
        min-idle: 10
        # 连接池最大连接数(使用负值表示没有限制)
        max-active: 100
        # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1
        database: 15
    

    因为在jar包RedisConfig.java中有取参数,如果没有启动报错

    然后在main方法中加入@EnableCaching开启缓存

    使用

    @RequestMapping(value="/getDomainKeyRedis", method = {RequestMethod.POST, RequestMethod.GET })
        @ResponseBody
        @ApiOperation(value="域管理根据域key查询redis",notes="根据参数域key,查询对应的数据,常用于下拉框")
        public DomainDto getDomainKeyRedis(HttpServletRequest request) {
            String key = request.getParameter("key");
    
            DomainDto domainDto = new DomainDto();
    
            if(StrUtil.isEmpty(key)){
                return domainDto;
            }
    
    //        redisUtils.get("sys_domain:"+key);
    
    //        domainDto = JSON.parseObject((byte[]) redisUtils.get("sys_domain:"+key),DomainDto.class);
    //        domainDto =  JSONObject.toJavaObject(JSONObject.parseObject(String.valueOf(redisUtils.get("sys_domain:"+key))),DomainDto.class);
    
           //根据缓存查询
            Object  obj= localCache.get("sys_domain:"+key);
            if(obj != null){
                domainDto = JSONObject.toJavaObject(JSONObject.parseObject(String.valueOf(obj)),DomainDto.class);
            }
    
            return domainDto;
        }
    

    接下来cloud-domain项目的流程说明

    cloud-domain项目引用了cloud-common的jar包,为保证每次项目启动的时候去redis获取到所有数据存储到缓存。MyStartupRunner.java实现了这个功能。
    在这里插入图片描述
    其实第一句清楚所有缓存可以去掉。但是无所谓了

    然后redis更新的时候,对应的cloud-domain项目中需要更新缓存。
    RedisMessageReceiver.java
    接收到消息,

    展开全文
  • 将Chrome缓存数据移出C盘

    千次阅读 2018-12-28 21:21:00
    Chrome浏览器会默认的将用户...用久了之后,就会积攒大量缓存数据与C盘内。尤其Win 10系统 C盘日益吃紧的前提下,将缓存是数据移至其他盘符很有必要。 1、进入 C:\Users\Administrator\AppData\Local\Google\Chro...

    Chrome浏览器会默认的将用户的缓存是数据存放于  C:\Users\你的用户名\AppData\Local\Google\Chrome\User Data文件夹内。用久了之后,就会积攒大量缓存数据与C盘内。尤其在Win 10系统 C盘日益吃紧的前提下,将缓存是数据移至其他盘符很有必要。

    1、进入 C:\Users\Administrator\AppData\Local\Google\Chrome (我这里的用户是Administrator)

    2、将User Data文件剪切到其他盘符,我这是在D盘内创建了一个叫做Chrome的文件夹。

    3、打开控制台窗口运行  mklink /j C:\Users\Administrator\AppData\Local\Google\Chrome\"User Data" "D:\chrome\User Data"

     

    该方法在win 7 和 win10上可以将缓存移出C盘

    至于XP上,由于没有mklink.exe 需要下载junction 来解决,下载junction 之后将其放置于windows\system32文件夹下

    然后重复上面第1和2这两个步骤。

    最后一步,运行 junction "C:\Documents and Settings\Administrator\Local Settings\Application Data\Google\Chrome\User Data" "D:\chrome\User Data"

     

     

    转载于:https://www.cnblogs.com/vichin/p/10192943.html

    展开全文
  • Java应用,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中。相对从数据库读取来说,读缓存效率会有很大提升。 集群环境下,常用的分布式缓存有Redis、Memcached等。但某些业务场景上,...

    使用场景
    在Java应用中,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中。相对从数据库中读取来说,读缓存效率会有很大提升。
    在集群环境下,常用的分布式缓存有Redis、Memcached等。但在某些业务场景上,可能不需要去搭建一套复杂的分布式缓存系统,在单机环境下,通常是会希望使用内部的缓存(LocalCache)。
    实现
    这里提供了两种LocalCache的实现,一种是基于ConcurrentHashMap实现基本本地缓存,另外一种是基于LinkedHashMap实现LRU策略的本地缓存。
    基于ConcurrentHashMap的实现

    static{
      timer = new Timer();
      map = new ConcurrentHashMap<>();
    }

    以ConcurrentHashMap作为缓存的存储结构。因为ConcurrentHashMap的线程安全的,所以基于此实现的LocalCache在多线程并发环境的操作是安全的。在JDK1.8中,ConcurrentHashMap是支持完全并发读,这对本地缓存的效率也是一种提升。通过调用ConcurrentHashMap对map的操作来实现对缓存的操作。
    【加群】:857565362
    私有构造函数

    private LocalCache() {
     
    }

    LocalCache是工具类,通过私有构造函数强化不可实例化的能力。
    缓存清除机制

    /**
     * 清除缓存任务类
     */
    static class CleanWorkerTask extends TimerTask {
     
      private String key;
     
      public CleanWorkerTask(String key) {
        this.key = key;
      }
     
      public void run() {
        LocalCache.remove(key);
      }
    }

    清理失效缓存是由Timer类实现的。内部类CleanWorkerTask继承于TimerTask用户清除缓存。每当新增一个元素的时候,都会调用timer.schedule加载清除缓存的任务。
    【加群】:857565362
    基于LinkedHashMap的实现
    以LinkedHashMap作为缓存的存储结构。主要是通过LinkedHashMap的按照访问顺序的特性来实现LRU策略。
    LRU
    LRU是Least Recently Used的缩写,即最近最久未使用。LRU缓存将会利用这个算法来淘汰缓存中老的数据元素,从而优化内存空间。
    基于LRU策略的map
    这里利用LinkedHashMap来实现基于LRU策略的map。通过调用父类LinkedHashMap的构造函数来实例化map。参数accessOrder设置为true保证其可以实现LRU策略。

    static class LRUMap<K, V> extends LinkedHashMap<K, V> {
     
        ... // 省略部分代码
     
        public LRUMap(int initialCapacity, float loadFactor) {
          super(initialCapacity, loadFactor, true);
        }
     
        ... // 省略部分代码
     
        /**
         * 重写LinkedHashMap中removeEldestEntry方法;
         * 新增元素的时候,会判断当前map大小是否超过DEFAULT_MAX_CAPACITY,超过则移除map中最老的节点;
         *
         * @param eldest
         * @return
         */
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
          return size() > DEFAULT_MAX_CAPACITY;
        }
     
      }

    线程安全

    /**
        * 读写锁
        */
       private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
     
       private final Lock rLock = readWriteLock.readLock();
     
       private final Lock wLock = readWriteLock.writeLock();

    LinkedHashMap并不是线程安全,如果不加控制的在多线程环境下使用的话,会有问题。所以在LRUMap中引入了ReentrantReadWriteLock读写锁,来控制并发问题。
    缓存淘汰机制

    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
         return size() > DEFAULT_MAX_CAPACITY;
       }

    此处重写LinkedHashMap中removeEldestEntry方法, 当缓存新增元素的时候,会判断当前map大小是否超过DEFAULT_MAX_CAPACITY,超过则移除map中最老的节点。
    【加群】:857565362
    缓存清除机制
    缓存清除机制与ConcurrentHashMap的实现一致,均是通过timer实现。
    我这儿整理了比较全面的JAVA相关的面试资料,
    需要领取面试资料的同学,请加群:473984645
    在这里插入图片描述
    获取更多学习资料,可以加群:473984645或扫描下方二维码
    在这里插入图片描述

    展开全文
  • 对于分析,可以使用Xdebug,它已定义的文件夹存储分析信息,并使用webgrind查看分析数据。我php.ini中为xdebug设置:zend_extension=C:/WEB/...

    对于分析,可以使用Xdebug,它在已定义的文件夹中存储分析信息,并使用webgrind查看分析数据。

    我在php.ini中为xdebug设置:

    zend_extension=C:/WEB/PHP-ts/php_xdebug-2.1.0-5.3-vc6.dll

    xdebug.collect_params=4

    xdebug.show_local_vars=on

    xdebug.scream=1

    xdebug.collect_vars=on

    xdebug.dump_globals=on

    xdebug.profiler_enable=1

    xdebug.profiler_output_dir=C:/WEB/_profiler/

    xdebug.profiler_output_name=cachegrind.%s.out

    xdebug.collect_return=1

    xdebug.collect_assignments=1

    xdebug.show_mem_delta=1我找到了一个关于缓存性能比较的blog post(但它是从2006年开始的!):

    Cache Type Cache Gets/sec

    Array Cache 365000

    APC Cache 98000

    File Cache 27000

    Memcached Cache (TCP/IP) 12200

    MySQL Query Cache (TCP/IP) 9900

    MySQL Query Cache (Unix Socket) 13500

    Selecting from table (TCP/IP) 5100

    Selecting from table (Unix Socket) 7400

    展开全文
  • 开发,需要前端记录些数据时,需要学习下缓存这些东西,获取用户名和密码的输入时,记录下数据内容,然后通过 setItem设置为缓存用的时候就可以直接用getItem来获取 这采用了local Storage. 简单的...
  • 使用场景Java应用,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中。相对从数据库读取来说,读缓存效率会有很大提升。集群环境下,常用的分布式缓存有Redis、Memcached等。但某些业务...
  • 何为缓存?本地缓存类似于map,当给定一个key,首先查找缓存中是否已经有值了,如果有则直接返回数据;相反如果不存在这样的key...(1)高并发的数据库访问时,为了抗住数据库并发连接压力,将数据缓存起来,当有请求
  • Google Guava--localcache本地缓存实例

    千次阅读 2015-02-12 21:08:08
    (1)高并发的数据库访问时,为了抗住数据库并发连接压力,将数据缓存起来,当有请求过来,直接返回数据; (2)当应用综的数据,更新周期较长,而且每次都查数据库的情况下,可以采用周期更新数据,从而有效减少...
  • 一. mybatis的一级缓存 首先我们来说一下mybatis的一级缓存。 mybatis使用默认的SQLSession——...当用户初次发起查询时,会从数据库查询数据,并将查询结果保存在localCache。 private <E> List&l
  • 这种方法比较简单方便的保存少的数据到到设备。你不用自己去管理这些数据,设备会自动管理他。 SharedObject  flash.net 包中,继承自EventDispatcher . ShareObject 和浏览器的cookie 类似,但是他...
  • Mybatis缓存

    2020-12-19 10:55:00
    这个SqlSession对象会创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询...
  • mybatis缓存

    2021-03-30 14:55:46
    Mybatis 使用到了两种缓存:本地缓存(local cache)和二级缓存(second level cache)。 每当一个新 session 被创建...默认情况下,本地缓存数据的生命周期等同于整个 session 的周期。由于缓存会被用来解决循环引用问
  • 当打开一个Unity目录之后,会C盘的…/AppDate/Local/cache文件产生两个缓存文件,一个是npm,一个是packages,用于缓存被打开的项目的一些数据的。删除之后,下次打开又会重新生成。 下图即为比较占用空间的缓存...
  • 操作数据库时需要创建SqlSession对象,对象有一个HashMap存储缓存数据,不同的SqlSession之间缓存的数据区域互不影响。每个SqlSession持有了Executor,每个Executor有一个LocalCache。当用户发起查询时,...
  • CPU多级缓存

    2020-07-26 12:20:32
    Local read:读本地缓存数据 Local write:写数据到本地缓存 Remote read:将内存的数据读取过来 Remote write:将数据写回到主存 多核时代,就容易导致顺序错乱问题,如果我们不做任何措施,结果会大不...
  • 我有Firefox作为我的默认浏览器,但它们仍然存储“ AppData\Local\Microsoft\Windows\Temporary Internet Files\Content.IE5 ”子文件夹 .以下是我如何确定这一事实 . 我首先通过将HKEY_LOCAL_MACHINE \ SYSTEM...
  • mybaits缓存

    2018-03-19 17:01:23
    同一个sqlsession,如果执行的语句是一样的,那么只有第一次的时候才会去查询数据库,后面都是将Local Cache的数据返回。 同一个sqlsession还有一个问题是,但执行更新,插入,删除等操作,对应的Local ...
  •  MyBatis会一次会话的表示 —— 一个SqlSession对象创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户...
  • 用于本地进程间通信创建套接字时...socket(PF_LOCAL,SOCK_DGRAM,0)分为流式套接字和用户数据包套接字unix域套接字(流式):server:int struct sockaddr_un结构体:sun_family:总是填充PF_UNIX或PF_LOCALsun_...
  • 秒拍视频其实你浏览器缓存

    千次阅读 2016-01-21 23:44:07
    我用的是chrome,那么地址栏输入chrome://version,这时你会看到chrome 的详细版本信息以及它存储数据路径,我那个是“个人资料路径” 然后按照这个路径找文件: C:\Users\\AppData\Local\Googl
  • Redis分布式缓存的实现 什么是缓存: 定义:就是计算机内存中一段数据 内存中数据特点: ...本地缓存:存在应用服务器内存中数据称之为本地缓存local cache) 分布式缓存:存储当前应用服务
  • Redis-分布式缓存实现

    2020-10-26 02:24:48
    1.什么是缓存(cache) 简单定义:就是计算机内存的一段数据。 2.内存中数据的特点 1.读写快 2....本地缓存:存在应用服务器内存中数据称之为本地缓存local cache)。 分布式缓存:存储当前应
  • 1. 场景 Java 应用,对于访问频率高,更新少的数据,通常的方案是将这类数据加入缓存中。相对从数据库读取来说,读缓存效率会有很大提升。集群环境下,常用的分布式缓存有 Redis、Memcached 等。但某些...
  • 最近在学这方面相关的内容,怕之后会忘记,写着记录一下 一.安装openresty 1.添加仓库执行命令 yum-config-manager --... 2.执行安装 yum install openresty 3.安装成功后 会默认的目录如下: /usr/local/op...
  • Mybatis缓存源码详解

    2019-12-30 17:05:46
    Mybatis缓存源码详解 ...Mybatis内部使用2级缓存便于加速数据的查询,降低...Mybatis的执行流程,首先去查找二级缓存,如果二级缓存中没有,再去一级缓存找,如果还没有,最后从数据库查询。 一级缓存示例 一级...
  • 局部变量表 局部变量表:Local Variables,被称之为局部...局部变量表所需的容量大小是编译期确定下来的,并保存方法的Code属性的maximum local variables数据方法运行期间是不会改变局部变量表的大小的。
  • 系统,有些数据数据量小,但是访问十分频繁(例如国家标准行政区域数据),针对这种场景,需要将数据搞到应用的本地缓存中,以提升系统的访问效率,减少无谓的数据库访问(数据库访问占用数据库连接,同时网络...
  • 设计架构缓存时,首先要选定缓存组件,比如要用 Local-Cache,还是 Redis、Memcached、Pika 等开源缓存组件,如果业务缓存需求比较特殊,还要考虑是否直接定制开发一个新的缓存组件,还是对开源缓存进行二次开发,...
  • ClickOnce 缓存概述

    2016-09-12 11:29:28
    所有 ClickOnce 应用程序,无论是本地安装的还是联机承载的,都存储客户端计算机上的 ClickOnce应用程序“缓存。ClickOnce 缓存是当前用户的 Documents and Settings 文件夹 Local Settings 目录下的一系列...
  • 多线程高并发场景往往是离不开cache的,需要根据不同的应用场景来需要选择不同的cache,比如分布式缓存如redis、memcached,还有本地(进程内)缓存如ehcache、GuavaCache、Caffeine、LocalCache等。 一、...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 289
精华内容 115
关键字:

缓存数据在local中