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,11 +6,12 @@ | ||
| 6 | 6 | ||
| 7 | <groupId>com.irrigation</groupId> | 7 | <groupId>com.irrigation</groupId> |
| 8 | <artifactId>common-lib</artifactId> | 8 | <artifactId>common-lib</artifactId> |
| 9 | - <version>1.0-SNAPSHOT</version> | 9 | + <version>1.2.0</version> |
| 10 | 10 | ||
| 11 | <properties> | 11 | <properties> |
| 12 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | 12 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
| 13 | <java.version>1.8</java.version> | 13 | <java.version>1.8</java.version> |
| 14 | + <spring-boot.version>1.5.10.RELEASE</spring-boot.version> | ||
| 14 | <swagger.version>2.7.0</swagger.version> | 15 | <swagger.version>2.7.0</swagger.version> |
| 15 | <lombok.version>1.16.20</lombok.version> | 16 | <lombok.version>1.16.20</lombok.version> |
| 16 | <commons-codec.version>1.11</commons-codec.version> | 17 | <commons-codec.version>1.11</commons-codec.version> |
| @@ -19,6 +20,8 @@ | @@ -19,6 +20,8 @@ | ||
| 19 | <fastjson.version>1.2.46</fastjson.version> | 20 | <fastjson.version>1.2.46</fastjson.version> |
| 20 | <spring-web.version>4.3.14.RELEASE</spring-web.version> | 21 | <spring-web.version>4.3.14.RELEASE</spring-web.version> |
| 21 | <spring-security-core.version>4.2.4.RELEASE</spring-security-core.version> | 22 | <spring-security-core.version>4.2.4.RELEASE</spring-security-core.version> |
| 23 | + <ehcache.version>2.6.11</ehcache.version> | ||
| 24 | + | ||
| 22 | </properties> | 25 | </properties> |
| 23 | 26 | ||
| 24 | 27 | ||
| @@ -83,6 +86,36 @@ | @@ -83,6 +86,36 @@ | ||
| 83 | <version>${spring-security-core.version}</version> | 86 | <version>${spring-security-core.version}</version> |
| 84 | <scope>provided</scope> | 87 | <scope>provided</scope> |
| 85 | </dependency> | 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 | </dependencies> | 120 | </dependencies> |
| 88 | <build> | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | +} |