Commit c01c0a10e5344eb093815a7998f4d47df722eda5
1 parent
8cc01016
feat:增加cache支持,并修改项目版本为1.2.0
Showing
10 changed files
with
615 additions
and
1 deletions
.gitignore
pom.xml
| ... | ... | @@ -6,11 +6,12 @@ |
| 6 | 6 | |
| 7 | 7 | <groupId>com.irrigation</groupId> |
| 8 | 8 | <artifactId>common-lib</artifactId> |
| 9 | - <version>1.0-SNAPSHOT</version> | |
| 9 | + <version>1.2.0</version> | |
| 10 | 10 | |
| 11 | 11 | <properties> |
| 12 | 12 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 13 | 13 | <java.version>1.8</java.version> |
| 14 | + <spring-boot.version>1.5.10.RELEASE</spring-boot.version> | |
| 14 | 15 | <swagger.version>2.7.0</swagger.version> |
| 15 | 16 | <lombok.version>1.16.20</lombok.version> |
| 16 | 17 | <commons-codec.version>1.11</commons-codec.version> |
| ... | ... | @@ -19,6 +20,8 @@ |
| 19 | 20 | <fastjson.version>1.2.46</fastjson.version> |
| 20 | 21 | <spring-web.version>4.3.14.RELEASE</spring-web.version> |
| 21 | 22 | <spring-security-core.version>4.2.4.RELEASE</spring-security-core.version> |
| 23 | + <ehcache.version>2.6.11</ehcache.version> | |
| 24 | + | |
| 22 | 25 | </properties> |
| 23 | 26 | |
| 24 | 27 | |
| ... | ... | @@ -83,6 +86,36 @@ |
| 83 | 86 | <version>${spring-security-core.version}</version> |
| 84 | 87 | <scope>provided</scope> |
| 85 | 88 | </dependency> |
| 89 | + <dependency> | |
| 90 | + <groupId>net.sf.ehcache</groupId> | |
| 91 | + <artifactId>ehcache-core</artifactId> | |
| 92 | + <version>${ehcache.version}</version> | |
| 93 | + <exclusions> | |
| 94 | + <exclusion> | |
| 95 | + <groupId>org.slf4j</groupId> | |
| 96 | + <artifactId>slf4j-api</artifactId> | |
| 97 | + </exclusion> | |
| 98 | + </exclusions> | |
| 99 | + <!--<scope>provided</scope>--> | |
| 100 | + </dependency> | |
| 101 | + <dependency> | |
| 102 | + <groupId>org.springframework.boot</groupId> | |
| 103 | + <artifactId>spring-boot-starter-cache</artifactId> | |
| 104 | + <version>${spring-boot.version}</version> | |
| 105 | + <!--<scope>provided</scope>--> | |
| 106 | + </dependency> | |
| 107 | + <dependency> | |
| 108 | + <groupId>org.springframework.boot</groupId> | |
| 109 | + <artifactId>spring-boot-starter-data-redis</artifactId> | |
| 110 | + <version>${spring-boot.version}</version> | |
| 111 | + <!--<scope>provided</scope>--> | |
| 112 | + </dependency> | |
| 113 | + <dependency> | |
| 114 | + <groupId>org.springframework.boot</groupId> | |
| 115 | + <artifactId>spring-boot-configuration-processor</artifactId> | |
| 116 | + <version>${spring-boot.version}</version> | |
| 117 | + <!--<scope>provided</scope>--> | |
| 118 | + </dependency> | |
| 86 | 119 | |
| 87 | 120 | </dependencies> |
| 88 | 121 | <build> | ... | ... |
src/main/java/com/irrigation/icl/cache/Command.java
0 → 100644
| 1 | +package com.irrigation.icl.cache; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +import java.io.Serializable; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * Command | |
| 9 | + */ | |
| 10 | +@Data | |
| 11 | +public class Command implements Serializable { | |
| 12 | + | |
| 13 | + private static final long serialVersionUID = 7126530485423286910L; | |
| 14 | + | |
| 15 | + // 设置本地缓存 | |
| 16 | + public final static byte OPT_SET = 0x01; | |
| 17 | + // 删除本地缓存Key | |
| 18 | + public final static byte OPT_DEL = 0x02; | |
| 19 | + // 删除本地缓存 | |
| 20 | + public final static byte OPT_REM = 0x03; | |
| 21 | + | |
| 22 | + public byte oper; | |
| 23 | + public String name; | |
| 24 | + public String key; | |
| 25 | + public String src; | |
| 26 | + | |
| 27 | + public Command() { | |
| 28 | + } | |
| 29 | + | |
| 30 | + public Command(String src, byte oper, String name, String key) { | |
| 31 | + this.src = src; | |
| 32 | + this.oper = oper; | |
| 33 | + this.name = name; | |
| 34 | + this.key = key; | |
| 35 | + } | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 更新本地缓存 | |
| 39 | + * | |
| 40 | + * @param cacheName | |
| 41 | + * @param key | |
| 42 | + * @return | |
| 43 | + */ | |
| 44 | + public static Command set(String src, String cacheName, String key) { | |
| 45 | + return new Command(src, OPT_SET, cacheName, key); | |
| 46 | + } | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * 删除本地缓存Key | |
| 50 | + * | |
| 51 | + * @param cacheName | |
| 52 | + * @param key | |
| 53 | + * @return | |
| 54 | + */ | |
| 55 | + public static Command del(String src, String cacheName, String key) { | |
| 56 | + return new Command(src, OPT_DEL, cacheName, key); | |
| 57 | + } | |
| 58 | + | |
| 59 | + /** | |
| 60 | + * 删除本地缓存 | |
| 61 | + * | |
| 62 | + * @param cacheName | |
| 63 | + * @return | |
| 64 | + */ | |
| 65 | + public static Command rem(String src, String cacheName) { | |
| 66 | + return new Command(src, OPT_REM, cacheName, null); | |
| 67 | + } | |
| 68 | +} | |
| 0 | 69 | \ No newline at end of file | ... | ... |
src/main/java/com/irrigation/icl/cache/LayeringCacheAutoFactory.java
0 → 100644
| 1 | +package com.irrigation.icl.cache; | |
| 2 | + | |
| 3 | +import com.irrigation.icl.cache.config.CacheConfig; | |
| 4 | +import com.irrigation.icl.cache.core.LayeringCacheLoadCondition; | |
| 5 | +import com.irrigation.icl.cache.core.LayeringCacheManager; | |
| 6 | +import com.irrigation.icl.cache.core.LayeringCacheProperties; | |
| 7 | +import lombok.extern.slf4j.Slf4j; | |
| 8 | +import org.springframework.boot.autoconfigure.AutoConfigureAfter; | |
| 9 | +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | |
| 10 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 11 | +import org.springframework.cache.CacheManager; | |
| 12 | +import org.springframework.context.annotation.Bean; | |
| 13 | +import org.springframework.context.annotation.Conditional; | |
| 14 | +import org.springframework.context.annotation.Configuration; | |
| 15 | +import org.springframework.data.redis.connection.Message; | |
| 16 | +import org.springframework.data.redis.connection.MessageListener; | |
| 17 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | |
| 18 | +import org.springframework.data.redis.core.RedisTemplate; | |
| 19 | +import org.springframework.data.redis.listener.PatternTopic; | |
| 20 | +import org.springframework.data.redis.listener.RedisMessageListenerContainer; | |
| 21 | +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * @Author: boni | |
| 25 | + * @Date: 2018/8/24-下午2:37 | |
| 26 | + */ | |
| 27 | +@Configuration | |
| 28 | +@Conditional(LayeringCacheLoadCondition.class) | |
| 29 | +@EnableConfigurationProperties(LayeringCacheProperties.class) | |
| 30 | +@Slf4j | |
| 31 | +public class LayeringCacheAutoFactory { | |
| 32 | + @Bean | |
| 33 | + public LayeringCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate, | |
| 34 | + LayeringCacheProperties layeringCacheProperties) { | |
| 35 | + LayeringCacheManager cacheManager = new LayeringCacheManager(redisTemplate, layeringCacheProperties); | |
| 36 | + cacheManager.setUsePrefix(true); | |
| 37 | + cacheManager.setDefaultExpiration(90); | |
| 38 | + return cacheManager; | |
| 39 | + } | |
| 40 | + | |
| 41 | + @Bean | |
| 42 | + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, | |
| 43 | + LayeringCacheProperties layeringCacheProperties, | |
| 44 | + MessageListenerAdapter listenerAdapter) { | |
| 45 | + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | |
| 46 | + container.setConnectionFactory(connectionFactory); | |
| 47 | + container.addMessageListener(listenerAdapter, new PatternTopic(layeringCacheProperties.getCacheL1().getTopic())); | |
| 48 | + | |
| 49 | + return container; | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Bean | |
| 53 | + LayeringCacheSyncCommandHandler layeringCacheSyncCommandHandler(CacheManager cacheManager) { | |
| 54 | + LayeringCacheSyncCommandHandler handler = new LayeringCacheSyncCommandHandler(cacheManager); | |
| 55 | + return handler; | |
| 56 | + } | |
| 57 | + | |
| 58 | + @Bean | |
| 59 | + MessageListenerAdapter listenerAdapter(LayeringCacheSyncCommandHandler handler, | |
| 60 | + RedisTemplate<Object, Object> redisTemplate) { | |
| 61 | + | |
| 62 | + return new MessageListenerAdapter(new MessageListener() { | |
| 63 | + @Override | |
| 64 | + public void onMessage(Message message, byte[] pattern) { | |
| 65 | + byte[] content = message.getBody(); | |
| 66 | + if (content != null) { | |
| 67 | + Command command = (Command) redisTemplate.getValueSerializer().deserialize(content); | |
| 68 | + if (command != null) { | |
| 69 | + log.info(command.toString()); | |
| 70 | + handler.handle(command); | |
| 71 | + } | |
| 72 | + } | |
| 73 | + } | |
| 74 | + | |
| 75 | + }); | |
| 76 | + } | |
| 77 | +} | |
| 0 | 78 | \ No newline at end of file | ... | ... |
src/main/java/com/irrigation/icl/cache/LayeringCacheSyncCommandHandler.java
0 → 100644
| 1 | +package com.irrigation.icl.cache; | |
| 2 | + | |
| 3 | +import com.irrigation.icl.cache.core.LayeringCache; | |
| 4 | +import lombok.Getter; | |
| 5 | +import lombok.extern.slf4j.Slf4j; | |
| 6 | +import org.springframework.cache.CacheManager; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * @Author: boni | |
| 10 | + * @Date: 2018/8/29-下午5:44 | |
| 11 | + */ | |
| 12 | +@Slf4j | |
| 13 | +@Getter | |
| 14 | +public class LayeringCacheSyncCommandHandler { | |
| 15 | + CacheManager cacheManager; | |
| 16 | + | |
| 17 | + public LayeringCacheSyncCommandHandler(CacheManager cacheManager) { | |
| 18 | + this.cacheManager = cacheManager; | |
| 19 | + } | |
| 20 | + | |
| 21 | + public void handle(Command command) { | |
| 22 | + if (cacheManager != null) { | |
| 23 | + LayeringCache layeringCache = (LayeringCache) cacheManager.getCache(command.getName()); | |
| 24 | + if (layeringCache == null) { | |
| 25 | + return; | |
| 26 | + } | |
| 27 | + layeringCache.updateL1Cache(command); | |
| 28 | + } | |
| 29 | + } | |
| 30 | +} | ... | ... |
src/main/java/com/irrigation/icl/cache/config/CacheConfig.java
0 → 100644
| 1 | +package com.irrigation.icl.cache.config; | |
| 2 | + | |
| 3 | +import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer; | |
| 4 | +import org.springframework.boot.autoconfigure.AutoConfigureAfter; | |
| 5 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | |
| 6 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | |
| 7 | +import org.springframework.context.annotation.Bean; | |
| 8 | +import org.springframework.context.annotation.Configuration; | |
| 9 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | |
| 10 | +import org.springframework.data.redis.core.RedisTemplate; | |
| 11 | +import org.springframework.data.redis.serializer.StringRedisSerializer; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * @Author: boni | |
| 15 | + * @Date: 2018/8/24-下午2:03 | |
| 16 | + */ | |
| 17 | +@Configuration | |
| 18 | +public class CacheConfig { | |
| 19 | + | |
| 20 | + @Bean | |
| 21 | + public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { | |
| 22 | + RedisTemplate<Object, Object> template = new RedisTemplate<>(); | |
| 23 | + template.setConnectionFactory(connectionFactory); | |
| 24 | + | |
| 25 | + //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 | |
| 26 | +// Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); | |
| 27 | + | |
| 28 | + GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer(); | |
| 29 | + | |
| 30 | +// ObjectMapper mapper = new ObjectMapper(); | |
| 31 | +// mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); | |
| 32 | +// mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); | |
| 33 | +// serializer.setObjectMapper(mapper); | |
| 34 | + | |
| 35 | + template.setValueSerializer(serializer); | |
| 36 | + //使用StringRedisSerializer来序列化和反序列化redis的key值 | |
| 37 | + template.setKeySerializer(new StringRedisSerializer()); | |
| 38 | + template.afterPropertiesSet(); | |
| 39 | + return template; | |
| 40 | + } | |
| 41 | + | |
| 42 | + | |
| 43 | +} | ... | ... |
src/main/java/com/irrigation/icl/cache/core/LayeringCache.java
0 → 100644
| 1 | +package com.irrigation.icl.cache.core; | |
| 2 | + | |
| 3 | +import com.irrigation.icl.cache.Command; | |
| 4 | +import lombok.extern.slf4j.Slf4j; | |
| 5 | +import net.sf.ehcache.CacheException; | |
| 6 | +import net.sf.ehcache.CacheManager; | |
| 7 | +import net.sf.ehcache.Ehcache; | |
| 8 | +import net.sf.ehcache.Element; | |
| 9 | +import net.sf.ehcache.config.CacheConfiguration; | |
| 10 | +import net.sf.ehcache.config.Configuration; | |
| 11 | +import net.sf.ehcache.config.DiskStoreConfiguration; | |
| 12 | +import net.sf.ehcache.config.PersistenceConfiguration; | |
| 13 | +import net.sf.ehcache.store.MemoryStoreEvictionPolicy; | |
| 14 | +import org.springframework.cache.Cache; | |
| 15 | +import org.springframework.cache.support.SimpleValueWrapper; | |
| 16 | +import org.springframework.data.redis.cache.RedisCache; | |
| 17 | +import org.springframework.data.redis.core.RedisOperations; | |
| 18 | +import org.springframework.util.StopWatch; | |
| 19 | + | |
| 20 | +import java.sql.Time; | |
| 21 | +import java.text.SimpleDateFormat; | |
| 22 | +import java.time.LocalDateTime; | |
| 23 | +import java.util.Date; | |
| 24 | +import java.util.concurrent.Callable; | |
| 25 | +import java.util.concurrent.ConcurrentHashMap; | |
| 26 | +import java.util.concurrent.Future; | |
| 27 | +import java.util.concurrent.FutureTask; | |
| 28 | + | |
| 29 | +/** | |
| 30 | + * @Author: boni | |
| 31 | + * @Date: 2018/8/24-下午2:27 | |
| 32 | + */ | |
| 33 | +@Slf4j | |
| 34 | +public class LayeringCache extends RedisCache { | |
| 35 | + private LayeringCacheProperties layeringCacheProperties; | |
| 36 | + private LayeringCacheManager layeringCacheManager; | |
| 37 | + private CacheManager layeringL1CacheManager; | |
| 38 | + private final ConcurrentHashMap<String, Future<Ehcache>> layeringL1CacheContainer = new ConcurrentHashMap<>(); | |
| 39 | + | |
| 40 | + private boolean layerL1Enable; | |
| 41 | + private String ID; | |
| 42 | +// private String HOST; | |
| 43 | +// private String CACHE_STORE; | |
| 44 | +// private String CACHE_STORE_SYNC; | |
| 45 | + | |
| 46 | + | |
| 47 | + public LayeringCache(LayeringCacheManager layeringCacheManager, String name, byte[] prefix, | |
| 48 | + RedisOperations<? extends Object, ? extends Object> redisOperations, long expiration) { | |
| 49 | + super(name, prefix, redisOperations, expiration); | |
| 50 | + this.layeringCacheManager = layeringCacheManager; | |
| 51 | + this.layeringCacheProperties = layeringCacheManager.getLayeringCacheProperties(); | |
| 52 | + this.layerL1Enable = layeringCacheProperties.getCacheL1().isEnable(); | |
| 53 | + initEhcache(); | |
| 54 | + } | |
| 55 | + | |
| 56 | + @Override | |
| 57 | + public Cache.ValueWrapper get(Object key) { | |
| 58 | + StopWatch stopWatch = new StopWatch(); | |
| 59 | + stopWatch.start(); | |
| 60 | + Ehcache ehCache = null; | |
| 61 | + if (layerL1Enable) { | |
| 62 | + ehCache = getL1Cache(super.getName()); | |
| 63 | + Element value = ehCache.get(key); | |
| 64 | + if (value != null) { | |
| 65 | + log.info("Hit Cache L1 (ehcache) :{}={}", key, value); | |
| 66 | + return (value != null ? new SimpleValueWrapper(value.getObjectValue()) : null); | |
| 67 | + } | |
| 68 | + } | |
| 69 | + Cache.ValueWrapper wrapper = super.get(key); | |
| 70 | + if (layerL1Enable && ehCache != null && wrapper != null) { | |
| 71 | + ehCache.put(new Element(key, wrapper.get())); | |
| 72 | + log.info("Hit Cache L2 (redis) :{}={}", key, wrapper); | |
| 73 | + } | |
| 74 | + stopWatch.stop(); | |
| 75 | + log.info("get use {} ms", stopWatch.getTotalTimeMillis()); | |
| 76 | + return wrapper; | |
| 77 | + } | |
| 78 | + | |
| 79 | + @Override | |
| 80 | + public void put(final Object key, final Object value) { | |
| 81 | + StopWatch stopWatch = new StopWatch(); | |
| 82 | + stopWatch.start(); | |
| 83 | + // 先更新1级然后再更新2级;允许短时间内的数据不一致 | |
| 84 | + if (layerL1Enable) { | |
| 85 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 86 | + ehCache.put(new Element(key, value, false, layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds(), layeringCacheProperties.getCacheL1().getLocalTimeToLiveSeconds())); | |
| 87 | + layeringCacheManager.publishMessage(Command.set(this.ID, super.getName(), String.valueOf(key))); | |
| 88 | + } | |
| 89 | + super.put(key, value); | |
| 90 | + stopWatch.stop(); | |
| 91 | + log.info("put use {} ms", stopWatch.getTotalTimeMillis()); | |
| 92 | + } | |
| 93 | + | |
| 94 | + @Override | |
| 95 | + public void evict(Object key) { | |
| 96 | + // 先删除2级缓存,然后再删除1级缓存; | |
| 97 | + super.evict(key); | |
| 98 | + if (layerL1Enable) { | |
| 99 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 100 | + ehCache.remove(key); | |
| 101 | + layeringCacheManager.publishMessage(Command.del(this.ID, super.getName(), String.valueOf(key))); | |
| 102 | + } | |
| 103 | + } | |
| 104 | + | |
| 105 | + @Override | |
| 106 | + public Cache.ValueWrapper putIfAbsent(Object key, final Object value) { | |
| 107 | + if (layerL1Enable) { | |
| 108 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 109 | + ehCache.putIfAbsent(new Element(key, value, false, layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds(), layeringCacheProperties.getCacheL1().getLocalTimeToLiveSeconds())); | |
| 110 | + layeringCacheManager.publishMessage(Command.set(this.ID, super.getName(), String.valueOf(key))); | |
| 111 | + } | |
| 112 | + Cache.ValueWrapper wrapper = super.putIfAbsent(key, value); | |
| 113 | + return wrapper; | |
| 114 | + } | |
| 115 | + | |
| 116 | + public void updateL1Cache(Command command) { | |
| 117 | + if (layerL1Enable && command != null && !this.ID.equals(command.getSrc())) { | |
| 118 | + switch (command.getOper()) { | |
| 119 | + case Command.OPT_DEL: | |
| 120 | + case Command.OPT_REM: | |
| 121 | + getL1Cache(command.getName()).remove(command.getKey()); | |
| 122 | + log.info("Clear Cache L1 {} - {}", command.getName(), command.getKey()); | |
| 123 | + break; | |
| 124 | + case Command.OPT_SET: | |
| 125 | + Cache.ValueWrapper wrapper = super.get(command.getKey()); | |
| 126 | + if (wrapper != null) { | |
| 127 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 128 | + ehCache.put(new Element(command.getKey(), wrapper.get(), false, layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds(), layeringCacheProperties.getCacheL1().getLocalTimeToLiveSeconds())); | |
| 129 | + log.info("Update Cache L1 {} - {}", command.getKey(), wrapper.get()); | |
| 130 | + } | |
| 131 | + break; | |
| 132 | + default: | |
| 133 | + break; | |
| 134 | + } | |
| 135 | + } | |
| 136 | + } | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * 创建本地缓存 | |
| 140 | + */ | |
| 141 | + private Ehcache getL1Cache(final String name) { | |
| 142 | + Future<Ehcache> future = this.layeringL1CacheContainer.get(name); | |
| 143 | + if (future == null) { | |
| 144 | + Callable<Ehcache> callable = () -> { | |
| 145 | + Ehcache cache = layeringL1CacheManager.getEhcache(name); | |
| 146 | + if (cache == null) { | |
| 147 | + layeringL1CacheManager.addCache(name); | |
| 148 | + cache = layeringL1CacheManager.getEhcache(name); | |
| 149 | + } | |
| 150 | + return cache; | |
| 151 | + }; | |
| 152 | + FutureTask<Ehcache> task = new FutureTask<>(callable); | |
| 153 | + future = this.layeringL1CacheContainer.putIfAbsent(name, task); | |
| 154 | + if (future == null) { | |
| 155 | + future = task; | |
| 156 | + task.run(); | |
| 157 | + } | |
| 158 | + } | |
| 159 | + try { | |
| 160 | + return future.get(); | |
| 161 | + } catch (Exception e) { | |
| 162 | + this.layeringL1CacheContainer.remove(name); | |
| 163 | + throw new CacheException(e); | |
| 164 | + } | |
| 165 | + | |
| 166 | + } | |
| 167 | + | |
| 168 | + private String getDate() { | |
| 169 | + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); | |
| 170 | + | |
| 171 | + return simpleDateFormat.format(new Date()); | |
| 172 | + } | |
| 173 | + | |
| 174 | + private void initEhcache() { | |
| 175 | + if (layerL1Enable) { | |
| 176 | + this.ID = layeringCacheProperties.getCacheL1().getKey() + "." + getDate(); | |
| 177 | +// this.HOST = Utils.getLocalHostIP(); | |
| 178 | +// this.CACHE_STORE = layeringCacheProperties.getCacheL1().getKey() | |
| 179 | +// + layeringCacheProperties.getCacheL1().getSeparator() | |
| 180 | +// + "cache" | |
| 181 | +// + layeringCacheProperties.getCacheL1().getSeparator() | |
| 182 | +// + "store"; | |
| 183 | +// this.CACHE_STORE_SYNC = this.CACHE_STORE + layeringCacheProperties.getCacheL1().getSeparator() + "sync"; | |
| 184 | + | |
| 185 | + Configuration configuration = new Configuration(); | |
| 186 | + configuration.setName(this.ID); | |
| 187 | + configuration.setMaxBytesLocalHeap(layeringCacheProperties.getCacheL1().getLocalMaxBytesLocalHeap()); | |
| 188 | + configuration.setMaxBytesLocalDisk(layeringCacheProperties.getCacheL1().getLocalMaxBytesLocalDisk()); | |
| 189 | + // DiskStore | |
| 190 | + // 每次启动设置新的文件地址,以避免重启期间一级缓存未同步,以及单机多应用启动造成EhcacheManager重复的问题. | |
| 191 | + DiskStoreConfiguration dsc = new DiskStoreConfiguration(); | |
| 192 | + dsc.setPath(layeringCacheProperties.getCacheL1().getLocalStoreLocation() + this.ID); | |
| 193 | + configuration.diskStore(dsc); | |
| 194 | + | |
| 195 | + PersistenceConfiguration persistenceConfiguration = new PersistenceConfiguration(); | |
| 196 | + persistenceConfiguration.setStrategy(PersistenceConfiguration.Strategy.NONE.name()); | |
| 197 | + | |
| 198 | + // Cache | |
| 199 | + CacheConfiguration cacheConfiguration = new CacheConfiguration(); | |
| 200 | + cacheConfiguration.setEternal(false); | |
| 201 | + | |
| 202 | + cacheConfiguration.persistence(persistenceConfiguration); | |
| 203 | + cacheConfiguration.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU); | |
| 204 | + cacheConfiguration.setDiskExpiryThreadIntervalSeconds(layeringCacheProperties.getCacheL1().getLocalDiskExpiryThreadIntervalSeconds()); | |
| 205 | + // 默认false,使用引用.设置为true,避免外部代码修改了缓存对象.造成EhCache的缓存对象也随之改变 | |
| 206 | + // 但是设置为true后,将引起element的tti不自动刷新.如果直接新建element去覆盖原值.则本地ttl和远程ttl会产生一定的误差. | |
| 207 | + // 因此,使用时放弃手动覆盖方式刷新本地tti,当本地tti过期后,自动从Redis中再获取即可. | |
| 208 | + cacheConfiguration.copyOnRead(true); | |
| 209 | + cacheConfiguration.copyOnWrite(true); | |
| 210 | + | |
| 211 | + cacheConfiguration.setTimeToIdleSeconds(layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds()); | |
| 212 | + cacheConfiguration.setTimeToLiveSeconds(layeringCacheProperties.getCacheL1().getLocalTimeToLiveSeconds()); | |
| 213 | + | |
| 214 | + configuration.setDefaultCacheConfiguration(cacheConfiguration); | |
| 215 | + configuration.setDynamicConfig(false); | |
| 216 | + configuration.setUpdateCheck(false); | |
| 217 | + | |
| 218 | + layeringL1CacheManager = new CacheManager(configuration); | |
| 219 | + } | |
| 220 | + } | |
| 221 | +} | ... | ... |
src/main/java/com/irrigation/icl/cache/core/LayeringCacheLoadCondition.java
0 → 100644
| 1 | +package com.irrigation.icl.cache.core; | |
| 2 | + | |
| 3 | +import org.springframework.boot.bind.RelaxedPropertyResolver; | |
| 4 | +import org.springframework.context.annotation.Condition; | |
| 5 | +import org.springframework.context.annotation.ConditionContext; | |
| 6 | +import org.springframework.core.type.AnnotatedTypeMetadata; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * @Author: boni | |
| 10 | + * @Date: 2018/8/24-下午2:52 | |
| 11 | + */ | |
| 12 | +public class LayeringCacheLoadCondition implements Condition { | |
| 13 | + @Override | |
| 14 | + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { | |
| 15 | + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( | |
| 16 | + context.getEnvironment(), "springext.cache."); | |
| 17 | + | |
| 18 | + Boolean enable = resolver.getProperty("enable",Boolean.class); | |
| 19 | + if(enable == null){ | |
| 20 | + return false; | |
| 21 | + } | |
| 22 | + return enable.booleanValue(); | |
| 23 | + | |
| 24 | + } | |
| 25 | +} | ... | ... |
src/main/java/com/irrigation/icl/cache/core/LayeringCacheManager.java
0 → 100644
| 1 | +package com.irrigation.icl.cache.core; | |
| 2 | + | |
| 3 | +import com.irrigation.icl.cache.Command; | |
| 4 | +import org.springframework.data.redis.cache.RedisCache; | |
| 5 | +import org.springframework.data.redis.cache.RedisCacheManager; | |
| 6 | +import org.springframework.data.redis.core.RedisOperations; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * @Author: boni | |
| 10 | + * @Date: 2018/8/24-下午2:29 | |
| 11 | + */ | |
| 12 | +public class LayeringCacheManager extends RedisCacheManager { | |
| 13 | + | |
| 14 | + private LayeringCacheProperties layeringCacheProperties; | |
| 15 | + | |
| 16 | + public LayeringCacheProperties getLayeringCacheProperties() { | |
| 17 | + return layeringCacheProperties; | |
| 18 | + } | |
| 19 | + | |
| 20 | + public void setLayeringCacheProperties(LayeringCacheProperties layeringCacheProperties) { | |
| 21 | + this.layeringCacheProperties = layeringCacheProperties; | |
| 22 | + } | |
| 23 | + | |
| 24 | + public LayeringCacheManager(RedisOperations redisOperations, LayeringCacheProperties layeringCacheProperties) { | |
| 25 | + super(redisOperations); | |
| 26 | + this.layeringCacheProperties = layeringCacheProperties; | |
| 27 | + } | |
| 28 | + | |
| 29 | + @SuppressWarnings("unchecked") | |
| 30 | + @Override | |
| 31 | + protected RedisCache createCache(String cacheName) { | |
| 32 | + long expiration = computeExpiration(cacheName); | |
| 33 | + return new LayeringCache( | |
| 34 | + this, | |
| 35 | + cacheName, | |
| 36 | + (this.isUsePrefix() ? this.getCachePrefix().prefix(cacheName) : null), | |
| 37 | + this.getRedisOperations(), | |
| 38 | + expiration); | |
| 39 | + } | |
| 40 | + | |
| 41 | + | |
| 42 | + //notify other redis clent to update L1 | |
| 43 | + public void publishMessage(Command command) { | |
| 44 | + this.getRedisOperations().convertAndSend(this.getLayeringCacheProperties().getCacheL1().getTopic(), command); | |
| 45 | + } | |
| 46 | + | |
| 47 | + | |
| 48 | +} | ... | ... |
src/main/java/com/irrigation/icl/cache/core/LayeringCacheProperties.java
0 → 100644
| 1 | +package com.irrigation.icl.cache.core; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * @Author: boni | |
| 8 | + * @Date: 2018/8/24-下午2:59 | |
| 9 | + */ | |
| 10 | + | |
| 11 | +@Data | |
| 12 | +@ConfigurationProperties(prefix = "springext.cache") | |
| 13 | +public class LayeringCacheProperties { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * L1缓存配置 | |
| 17 | + */ | |
| 18 | + private L1 cacheL1; | |
| 19 | + private boolean enable = true; | |
| 20 | + | |
| 21 | + @Data | |
| 22 | + // ehcache设置 | |
| 23 | + public static class L1 { | |
| 24 | + | |
| 25 | + /** | |
| 26 | + * 是否启用L1缓存 | |
| 27 | + */ | |
| 28 | + private boolean enable = false; | |
| 29 | + /** | |
| 30 | + * 启用L1缓存后同步的topic | |
| 31 | + */ | |
| 32 | + private String topic = "default_L1_sync_topic"; | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * cache中使用的key前缀 | |
| 36 | + */ | |
| 37 | + private String key = "L1"; | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * key分隔符 | |
| 41 | + */ | |
| 42 | + private String separator = ":"; | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * L1缓存存储磁盘位置 | |
| 46 | + */ | |
| 47 | + private String localStoreLocation = "./cache/"; | |
| 48 | + /** | |
| 49 | + * 本地缓存最大内存大小 | |
| 50 | + */ | |
| 51 | + private String localMaxBytesLocalHeap = "128M"; | |
| 52 | + /** | |
| 53 | + * 本地缓存最大磁盘大小 | |
| 54 | + */ | |
| 55 | + private String localMaxBytesLocalDisk = "1024M"; | |
| 56 | + | |
| 57 | + /** | |
| 58 | + * 本地缓存过期时间,单位秒,默认10分钟 | |
| 59 | + */ | |
| 60 | + private int localTimeToLiveSeconds = 1 * 60; | |
| 61 | + private int localTimeToIdleSeconds = 0; | |
| 62 | + /** | |
| 63 | + * 本地缓存清理周期,单位秒,默认3分钟 | |
| 64 | + */ | |
| 65 | + private int localDiskExpiryThreadIntervalSeconds = 90; | |
| 66 | + } | |
| 67 | +} | ... | ... |