Commit 108f8f8c047654cfb4ae56fb7bba85ffa17ef30d
Merge branch 'dev'
Showing
17 changed files
with
772 additions
and
14 deletions
.gitignore
README.md
| 1 | -sass-common-lib:公共项目库 | |
| 1 | +## sass-common-lib:公共项目库 | |
| 2 | + | |
| 3 | +### v 1.2.0版本更新记录 | |
| 4 | +- 利用redis和ehcache增加了缓存支持,redis为L2级缓存,ehcache为L1级cache; | |
| 5 | +> application.yml基本配置如下: | |
| 6 | +```json | |
| 7 | +#开启ehcache和redis两级缓存 | |
| 8 | +springext: | |
| 9 | + cache: | |
| 10 | + enable: true | |
| 11 | + cacheL1: | |
| 12 | + enable: true | |
| 13 | + topic: cache #同步L1缓存的topic | |
| 14 | + key: "keys" | |
| 15 | +``` | |
| 16 | +```json | |
| 17 | +#redis配置 | |
| 18 | +spring: | |
| 19 | + redis: | |
| 20 | + database: 10 | |
| 21 | + host: 192.168.2.252 | |
| 22 | + port: 6379 | |
| 23 | +# password: zhnf@123 | |
| 24 | + pool: | |
| 25 | + max-wait: 1 | |
| 26 | +``` | |
| 27 | + | |
| 28 | +### v1.2.1 | |
| 29 | +- 增加了KeyGenerator实现类,实现类中使用className.methodName:params拼接成key | |
| 30 | + | |
| 31 | +### v1.2.2 | |
| 32 | +- 增加了对@CacheEvict(allEntries = true)的支持 | |
| 2 | 33 | \ No newline at end of file | ... | ... |
pom.xml
| ... | ... | @@ -6,19 +6,22 @@ |
| 6 | 6 | |
| 7 | 7 | <groupId>com.irrigation</groupId> |
| 8 | 8 | <artifactId>common-lib</artifactId> |
| 9 | - <version>1.0-SNAPSHOT</version> | |
| 9 | + <version>1.2.4</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> |
| 17 | 18 | <guava.vsersion>20.0</guava.vsersion> |
| 18 | 19 | <slf4j.version>1.7.24</slf4j.version> |
| 19 | - <fastjson.version>1.2.46</fastjson.version> | |
| 20 | + <fastjson.version>1.2.49</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.core.LayeringCacheLoadCondition; | |
| 4 | +import com.irrigation.icl.cache.core.LayeringCacheManager; | |
| 5 | +import com.irrigation.icl.cache.core.LayeringCacheProperties; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 8 | +import org.springframework.cache.CacheManager; | |
| 9 | +import org.springframework.context.annotation.Bean; | |
| 10 | +import org.springframework.context.annotation.Conditional; | |
| 11 | +import org.springframework.context.annotation.Configuration; | |
| 12 | +import org.springframework.data.redis.connection.Message; | |
| 13 | +import org.springframework.data.redis.connection.MessageListener; | |
| 14 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | |
| 15 | +import org.springframework.data.redis.core.RedisTemplate; | |
| 16 | +import org.springframework.data.redis.listener.PatternTopic; | |
| 17 | +import org.springframework.data.redis.listener.RedisMessageListenerContainer; | |
| 18 | +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; | |
| 19 | + | |
| 20 | +/** | |
| 21 | + * @Author: boni | |
| 22 | + * @Date: 2018/8/24-下午2:37 | |
| 23 | + */ | |
| 24 | +@Configuration | |
| 25 | +@Conditional(LayeringCacheLoadCondition.class) | |
| 26 | +@EnableConfigurationProperties(LayeringCacheProperties.class) | |
| 27 | +@Slf4j | |
| 28 | +public class LayeringCacheAutoFactory { | |
| 29 | + @Bean | |
| 30 | + public LayeringCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate, | |
| 31 | + LayeringCacheProperties layeringCacheProperties) { | |
| 32 | + LayeringCacheManager cacheManager = new LayeringCacheManager(redisTemplate, layeringCacheProperties); | |
| 33 | + cacheManager.setUsePrefix(true); | |
| 34 | + long expire = layeringCacheProperties.getExpire() > 0L ? layeringCacheProperties.getExpire() : 120L; | |
| 35 | + cacheManager.setExpires(layeringCacheProperties.getExpires()); | |
| 36 | + cacheManager.setDefaultExpiration(expire); | |
| 37 | + return cacheManager; | |
| 38 | + } | |
| 39 | + | |
| 40 | + @Bean | |
| 41 | + RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, | |
| 42 | + LayeringCacheProperties layeringCacheProperties, | |
| 43 | + MessageListenerAdapter listenerAdapter) { | |
| 44 | + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); | |
| 45 | + container.setConnectionFactory(connectionFactory); | |
| 46 | + container.addMessageListener(listenerAdapter, new PatternTopic(layeringCacheProperties.getCacheL1().getTopic())); | |
| 47 | + | |
| 48 | + return container; | |
| 49 | + } | |
| 50 | + | |
| 51 | + @Bean | |
| 52 | + LayeringCacheSyncCommandHandler layeringCacheSyncCommandHandler(CacheManager cacheManager) { | |
| 53 | + LayeringCacheSyncCommandHandler handler = new LayeringCacheSyncCommandHandler(cacheManager); | |
| 54 | + return handler; | |
| 55 | + } | |
| 56 | + | |
| 57 | + @Bean | |
| 58 | + MessageListenerAdapter listenerAdapter(LayeringCacheSyncCommandHandler handler, | |
| 59 | + RedisTemplate<Object, Object> redisTemplate) { | |
| 60 | + | |
| 61 | + return new MessageListenerAdapter(new MessageListener() { | |
| 62 | + @Override | |
| 63 | + public void onMessage(Message message, byte[] pattern) { | |
| 64 | + byte[] content = message.getBody(); | |
| 65 | + if (content != null) { | |
| 66 | +// String commandString = new String(content, Charset.defaultCharset()); | |
| 67 | +// Command command = JSON.parseObject(commandString, Command.class); | |
| 68 | + Command command = (Command) redisTemplate.getValueSerializer().deserialize(content); | |
| 69 | + if (command != null) { | |
| 70 | + log.debug(command.toString()); | |
| 71 | + handler.handle(command); | |
| 72 | + } | |
| 73 | + } | |
| 74 | + } | |
| 75 | + | |
| 76 | + }); | |
| 77 | + } | |
| 78 | +} | |
| 0 | 79 | \ 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 org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |
| 4 | +import org.springframework.context.annotation.Bean; | |
| 5 | +import org.springframework.context.annotation.Configuration; | |
| 6 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | |
| 7 | +import org.springframework.data.redis.core.RedisTemplate; | |
| 8 | +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; | |
| 9 | +import org.springframework.data.redis.serializer.StringRedisSerializer; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * @Author: boni | |
| 13 | + * @Date: 2018/8/24-下午2:03 | |
| 14 | + */ | |
| 15 | +@Configuration | |
| 16 | +public class CacheConfig { | |
| 17 | + | |
| 18 | + @ConditionalOnMissingBean(RedisTemplate.class) | |
| 19 | + @Bean | |
| 20 | + public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { | |
| 21 | + RedisTemplate<Object, Object> template = new RedisTemplate<>(); | |
| 22 | + template.setConnectionFactory(connectionFactory); | |
| 23 | +// Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); | |
| 24 | +// ObjectMapper om = new ObjectMapper(); | |
| 25 | +// om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); | |
| 26 | +// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); | |
| 27 | +// jackson2JsonRedisSerializer.setObjectMapper(om); | |
| 28 | +// | |
| 29 | + template.setValueSerializer(new JdkSerializationRedisSerializer()); | |
| 30 | + //使用StringRedisSerializer来序列化和反序列化redis的key值 | |
| 31 | + template.setKeySerializer(new StringRedisSerializer()); | |
| 32 | + template.afterPropertiesSet(); | |
| 33 | + return template; | |
| 34 | + } | |
| 35 | + | |
| 36 | +} | ... | ... |
src/main/java/com/irrigation/icl/cache/core/CustomCacheKeyGenerator.java
0 → 100644
| 1 | +package com.irrigation.icl.cache.core; | |
| 2 | + | |
| 3 | +import lombok.extern.slf4j.Slf4j; | |
| 4 | +import org.springframework.cache.interceptor.KeyGenerator; | |
| 5 | +import org.springframework.util.StringUtils; | |
| 6 | + | |
| 7 | +import java.lang.reflect.Method; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * @Author: boni | |
| 11 | + * @Date: 2018/9/4-下午3:20 | |
| 12 | + */ | |
| 13 | +@Slf4j | |
| 14 | +public class CustomCacheKeyGenerator implements KeyGenerator { | |
| 15 | + public static final int NO_PARAM_KEY = 0; | |
| 16 | + | |
| 17 | + @Override | |
| 18 | + public Object generate(Object target, Method method, Object... params) { | |
| 19 | + StringBuilder key = new StringBuilder(); | |
| 20 | + key.append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":"); | |
| 21 | + if (params.length == 0) { | |
| 22 | + return key.append(NO_PARAM_KEY).toString(); | |
| 23 | + } | |
| 24 | + key.append(StringUtils.arrayToCommaDelimitedString(params)); | |
| 25 | + //String finalKey = key.toString(); | |
| 26 | + //log.debug("using cache key={}", finalKey); | |
| 27 | + return key.toString(); | |
| 28 | + } | |
| 29 | +} | ... | ... |
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.text.SimpleDateFormat; | |
| 21 | +import java.util.Date; | |
| 22 | +import java.util.concurrent.Callable; | |
| 23 | +import java.util.concurrent.ConcurrentHashMap; | |
| 24 | +import java.util.concurrent.Future; | |
| 25 | +import java.util.concurrent.FutureTask; | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * @Author: boni | |
| 29 | + * @Date: 2018/8/24-下午2:27 | |
| 30 | + */ | |
| 31 | +@Slf4j | |
| 32 | +public class LayeringCache extends RedisCache { | |
| 33 | + private final ConcurrentHashMap<String, Future<Ehcache>> layeringL1CacheContainer = new ConcurrentHashMap<>(); | |
| 34 | + private LayeringCacheProperties layeringCacheProperties; | |
| 35 | + private LayeringCacheManager layeringCacheManager; | |
| 36 | + private CacheManager layeringL1CacheManager; | |
| 37 | + private boolean layerL1Enable; | |
| 38 | + private String ID; | |
| 39 | +// private String HOST; | |
| 40 | +// private String CACHE_STORE; | |
| 41 | +// private String CACHE_STORE_SYNC; | |
| 42 | + | |
| 43 | + | |
| 44 | + public LayeringCache(LayeringCacheManager layeringCacheManager, String name, byte[] prefix, | |
| 45 | + RedisOperations<? extends Object, ? extends Object> redisOperations, long expiration) { | |
| 46 | + super(name, prefix, redisOperations, expiration); | |
| 47 | + this.layeringCacheManager = layeringCacheManager; | |
| 48 | + this.layeringCacheProperties = layeringCacheManager.getLayeringCacheProperties(); | |
| 49 | + this.layerL1Enable = layeringCacheProperties.getCacheL1().isEnable(); | |
| 50 | + initEhcache(); | |
| 51 | + } | |
| 52 | + | |
| 53 | + @Override | |
| 54 | + public Cache.ValueWrapper get(Object key) { | |
| 55 | + StopWatch stopWatch = new StopWatch(); | |
| 56 | + stopWatch.start(); | |
| 57 | + Ehcache ehCache = null; | |
| 58 | + if (layerL1Enable) { | |
| 59 | + ehCache = getL1Cache(super.getName()); | |
| 60 | + Element value = ehCache.get(key); | |
| 61 | + if (value != null) { | |
| 62 | + log.debug("Hit Cache L1 (ehcache) :{}={}", key, value); | |
| 63 | + return new SimpleValueWrapper(value.getObjectValue()); | |
| 64 | + } | |
| 65 | + } | |
| 66 | + Cache.ValueWrapper wrapper = super.get(key); | |
| 67 | + if (layerL1Enable && ehCache != null && wrapper != null) { | |
| 68 | + ehCache.put(new Element(key, wrapper.get())); | |
| 69 | + log.debug("Hit Cache L2 (redis) :{}={}", key, wrapper); | |
| 70 | + } | |
| 71 | + stopWatch.stop(); | |
| 72 | + log.debug("get use {} ms", stopWatch.getTotalTimeMillis()); | |
| 73 | + return wrapper; | |
| 74 | + } | |
| 75 | + | |
| 76 | + @Override | |
| 77 | + public void put(final Object key, final Object value) { | |
| 78 | + StopWatch stopWatch = new StopWatch(); | |
| 79 | + stopWatch.start(); | |
| 80 | + // 先更新1级然后再更新2级;允许短时间内的数据不一致 | |
| 81 | + if (layerL1Enable) { | |
| 82 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 83 | + ehCache.put(new Element(key, value, false, layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds(), layeringCacheProperties.getCacheL1() | |
| 84 | + .getLocalTimeToLiveSeconds())); | |
| 85 | + layeringCacheManager.publishMessage(Command.set(this.ID, super.getName(), String.valueOf(key))); | |
| 86 | + } | |
| 87 | + super.put(key, value); | |
| 88 | + stopWatch.stop(); | |
| 89 | + log.debug("put use {} ms", stopWatch.getTotalTimeMillis()); | |
| 90 | + } | |
| 91 | + | |
| 92 | + @Override | |
| 93 | + public void evict(Object key) { | |
| 94 | + // 先删除2级缓存,然后再删除1级缓存; | |
| 95 | + super.evict(key); | |
| 96 | + if (layerL1Enable) { | |
| 97 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 98 | + ehCache.remove(key); | |
| 99 | + layeringCacheManager.publishMessage(Command.del(this.ID, super.getName(), String.valueOf(key))); | |
| 100 | + } | |
| 101 | + } | |
| 102 | + | |
| 103 | + @Override | |
| 104 | + public Cache.ValueWrapper putIfAbsent(Object key, final Object value) { | |
| 105 | + if (layerL1Enable) { | |
| 106 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 107 | + ehCache.putIfAbsent(new Element(key, value, false, layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds(), layeringCacheProperties | |
| 108 | + .getCacheL1().getLocalTimeToLiveSeconds())); | |
| 109 | + layeringCacheManager.publishMessage(Command.set(this.ID, super.getName(), String.valueOf(key))); | |
| 110 | + } | |
| 111 | + Cache.ValueWrapper wrapper = super.putIfAbsent(key, value); | |
| 112 | + return wrapper; | |
| 113 | + } | |
| 114 | + | |
| 115 | + @Override | |
| 116 | + public void clear() { | |
| 117 | + super.clear(); | |
| 118 | + if (layerL1Enable) { | |
| 119 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 120 | + ehCache.removeAll(); | |
| 121 | + layeringCacheManager.publishMessage(Command.rem(this.ID, super.getName())); | |
| 122 | + } | |
| 123 | + } | |
| 124 | + | |
| 125 | + public void updateL1Cache(Command command) { | |
| 126 | + if (layerL1Enable && command != null && !this.ID.equals(command.getSrc())) { | |
| 127 | + switch (command.getOper()) { | |
| 128 | + case Command.OPT_DEL: | |
| 129 | + getL1Cache(command.getName()).remove(command.getKey()); | |
| 130 | + log.debug("Remove Cache L1 {} - {}", command.getName(), command.getKey()); | |
| 131 | + break; | |
| 132 | + case Command.OPT_REM: | |
| 133 | + getL1Cache(command.getName()).removeAll(); | |
| 134 | + log.debug("Clear Cache L1 {}", command.getName()); | |
| 135 | + break; | |
| 136 | + case Command.OPT_SET: | |
| 137 | + Cache.ValueWrapper wrapper = super.get(command.getKey()); | |
| 138 | + if (wrapper != null) { | |
| 139 | + Ehcache ehCache = getL1Cache(super.getName()); | |
| 140 | + ehCache.put(new Element(command.getKey(), wrapper.get(), false, layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds(), | |
| 141 | + layeringCacheProperties.getCacheL1().getLocalTimeToLiveSeconds())); | |
| 142 | + log.debug("Update Cache L1 {} - {}", command.getKey(), wrapper.get()); | |
| 143 | + } | |
| 144 | + break; | |
| 145 | + default: | |
| 146 | + break; | |
| 147 | + } | |
| 148 | + } | |
| 149 | + } | |
| 150 | + | |
| 151 | + /** | |
| 152 | + * 创建本地缓存 | |
| 153 | + */ | |
| 154 | + private Ehcache getL1Cache(final String name) { | |
| 155 | + Future<Ehcache> future = this.layeringL1CacheContainer.get(name); | |
| 156 | + if (future == null) { | |
| 157 | + Callable<Ehcache> callable = () -> { | |
| 158 | + Ehcache cache = layeringL1CacheManager.getCache(name); | |
| 159 | + if (cache == null) { | |
| 160 | + CacheConfiguration cacheConfiguration = getCacheConfiguration(); | |
| 161 | + cacheConfiguration.setName(name); | |
| 162 | + Ehcache ehcache = new net.sf.ehcache.Cache(cacheConfiguration); | |
| 163 | + layeringL1CacheManager.addCache(ehcache); | |
| 164 | + cache = layeringL1CacheManager.getEhcache(name); | |
| 165 | + } | |
| 166 | + return cache; | |
| 167 | + }; | |
| 168 | + FutureTask<Ehcache> task = new FutureTask<>(callable); | |
| 169 | + future = this.layeringL1CacheContainer.putIfAbsent(name, task); | |
| 170 | + if (future == null) { | |
| 171 | + future = task; | |
| 172 | + task.run(); | |
| 173 | + } | |
| 174 | + } | |
| 175 | + try { | |
| 176 | + return future.get(); | |
| 177 | + } catch (Exception e) { | |
| 178 | + this.layeringL1CacheContainer.remove(name); | |
| 179 | + throw new CacheException(e); | |
| 180 | + } | |
| 181 | + | |
| 182 | + } | |
| 183 | + | |
| 184 | + | |
| 185 | + private String getDate() { | |
| 186 | + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); | |
| 187 | + | |
| 188 | + return simpleDateFormat.format(new Date()); | |
| 189 | + } | |
| 190 | + | |
| 191 | + private CacheConfiguration getCacheConfiguration() { | |
| 192 | + // Cache | |
| 193 | + CacheConfiguration cacheConfiguration = new CacheConfiguration(); | |
| 194 | + cacheConfiguration.setEternal(false); | |
| 195 | + | |
| 196 | + PersistenceConfiguration persistenceConfiguration = new PersistenceConfiguration(); | |
| 197 | + persistenceConfiguration.setStrategy(PersistenceConfiguration.Strategy.NONE.name()); | |
| 198 | + | |
| 199 | + cacheConfiguration.persistence(persistenceConfiguration); | |
| 200 | + cacheConfiguration.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU); | |
| 201 | + cacheConfiguration.setDiskExpiryThreadIntervalSeconds(layeringCacheProperties.getCacheL1().getLocalDiskExpiryThreadIntervalSeconds()); | |
| 202 | + // 默认false,使用引用.设置为true,避免外部代码修改了缓存对象.造成EhCache的缓存对象也随之改变 | |
| 203 | + // 但是设置为true后,将引起element的tti不自动刷新.如果直接新建element去覆盖原值.则本地ttl和远程ttl会产生一定的误差. | |
| 204 | + // 因此,使用时放弃手动覆盖方式刷新本地tti,当本地tti过期后,自动从Redis中再获取即可. | |
| 205 | + cacheConfiguration.copyOnRead(true); | |
| 206 | + cacheConfiguration.copyOnWrite(true); | |
| 207 | + | |
| 208 | +// CopyStrategyConfiguration copyStrategyConfiguration = new CopyStrategyConfiguration(); | |
| 209 | +// copyStrategyConfiguration.setClass(MyCopyStrategy.class.getName()); | |
| 210 | +// copyStrategyConfiguration.setCopyStrategyInstance(new MyCopyStrategy()); | |
| 211 | + | |
| 212 | +// cacheConfiguration.addCopyStrategy(copyStrategyConfiguration); | |
| 213 | + | |
| 214 | + cacheConfiguration.setTimeToIdleSeconds(layeringCacheProperties.getCacheL1().getLocalTimeToIdleSeconds()); | |
| 215 | + cacheConfiguration.setTimeToLiveSeconds(layeringCacheProperties.getCacheL1().getLocalTimeToLiveSeconds()); | |
| 216 | + | |
| 217 | + return cacheConfiguration; | |
| 218 | + } | |
| 219 | + | |
| 220 | + private void initEhcache() { | |
| 221 | + if (layerL1Enable) { | |
| 222 | + this.ID = layeringCacheProperties.getCacheL1().getKey() + "." + getDate(); | |
| 223 | +// this.HOST = Utils.getLocalHostIP(); | |
| 224 | +// this.CACHE_STORE = layeringCacheProperties.getCacheL1().getKey() | |
| 225 | +// + layeringCacheProperties.getCacheL1().getSeparator() | |
| 226 | +// + "cache" | |
| 227 | +// + layeringCacheProperties.getCacheL1().getSeparator() | |
| 228 | +// + "store"; | |
| 229 | +// this.CACHE_STORE_SYNC = this.CACHE_STORE + layeringCacheProperties.getCacheL1().getSeparator() + "sync"; | |
| 230 | + | |
| 231 | + Configuration configuration = new Configuration(); | |
| 232 | + configuration.setName(this.ID); | |
| 233 | + configuration.setMaxBytesLocalHeap(layeringCacheProperties.getCacheL1().getLocalMaxBytesLocalHeap()); | |
| 234 | + configuration.setMaxBytesLocalDisk(layeringCacheProperties.getCacheL1().getLocalMaxBytesLocalDisk()); | |
| 235 | + // DiskStore | |
| 236 | + // 每次启动设置新的文件地址,以避免重启期间一级缓存未同步,以及单机多应用启动造成EhcacheManager重复的问题. | |
| 237 | + DiskStoreConfiguration dsc = new DiskStoreConfiguration(); | |
| 238 | + dsc.setPath(layeringCacheProperties.getCacheL1().getLocalStoreLocation() + this.ID); | |
| 239 | + configuration.diskStore(dsc); | |
| 240 | + | |
| 241 | + CacheConfiguration cacheConfiguration = getCacheConfiguration(); | |
| 242 | + | |
| 243 | + configuration.setDefaultCacheConfiguration(cacheConfiguration); | |
| 244 | + configuration.setDynamicConfig(false); | |
| 245 | + configuration.setUpdateCheck(false); | |
| 246 | + | |
| 247 | + | |
| 248 | + layeringL1CacheManager = new CacheManager(configuration); | |
| 249 | + } | |
| 250 | + } | |
| 251 | +// | |
| 252 | +// class MyCopyStrategy implements ReadWriteCopyStrategy<Element> { | |
| 253 | +// @Override | |
| 254 | +// public Element copyForWrite(Element value) { | |
| 255 | +// if (value != null) { | |
| 256 | +// Object temp = (Serializable) value.getObjectValue(); | |
| 257 | +// return new Element(value.getObjectKey(), temp); | |
| 258 | +// } | |
| 259 | +// return value; | |
| 260 | +// } | |
| 261 | +// | |
| 262 | +// @Override | |
| 263 | +// public Element copyForRead(Element storedValue) { | |
| 264 | +// if (storedValue != null) { | |
| 265 | +// Object temp = (Serializable) storedValue.getObjectValue(); | |
| 266 | +// return new Element(storedValue.getObjectKey(), temp); | |
| 267 | +// } | |
| 268 | +// return storedValue; | |
| 269 | +// | |
| 270 | +// } | |
| 271 | +// } | |
| 272 | +} | ... | ... |
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 | +import java.util.Map; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * @Author: boni | |
| 10 | + * @Date: 2018/8/24-下午2:59 | |
| 11 | + */ | |
| 12 | + | |
| 13 | +@Data | |
| 14 | +@ConfigurationProperties(prefix = "springext.cache") | |
| 15 | +public class LayeringCacheProperties { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * L1缓存配置 | |
| 19 | + */ | |
| 20 | + private L1 cacheL1; | |
| 21 | + /** | |
| 22 | + * 是否启用缓存 | |
| 23 | + */ | |
| 24 | + private boolean enable = true; | |
| 25 | + /** | |
| 26 | + * L2级缓存的过期时间,单位秒 | |
| 27 | + */ | |
| 28 | + private long expire; | |
| 29 | + | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * L2级缓存的过期时间,name :time | |
| 33 | + */ | |
| 34 | + private Map<String, Long> expires; | |
| 35 | + | |
| 36 | + | |
| 37 | + @Data | |
| 38 | + // ehcache设置 | |
| 39 | + public static class L1 { | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * 是否启用L1缓存 | |
| 43 | + */ | |
| 44 | + private boolean enable = false; | |
| 45 | + /** | |
| 46 | + * 启用L1缓存后同步的topic | |
| 47 | + */ | |
| 48 | + private String topic = "default_L1_sync_topic"; | |
| 49 | + | |
| 50 | + /** | |
| 51 | + * cache中使用的key前缀 | |
| 52 | + */ | |
| 53 | + private String key = "L1"; | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * key分隔符 | |
| 57 | + */ | |
| 58 | + private String separator = ":"; | |
| 59 | + | |
| 60 | + /** | |
| 61 | + * L1缓存存储磁盘位置 | |
| 62 | + */ | |
| 63 | + private String localStoreLocation = "./cache/"; | |
| 64 | + /** | |
| 65 | + * 本地缓存最大内存大小 | |
| 66 | + */ | |
| 67 | + private String localMaxBytesLocalHeap = "128M"; | |
| 68 | + /** | |
| 69 | + * 本地缓存最大磁盘大小 | |
| 70 | + */ | |
| 71 | + private String localMaxBytesLocalDisk = "1024M"; | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * 本地缓存过期时间,单位秒,默认10分钟 | |
| 75 | + */ | |
| 76 | + private int localTimeToLiveSeconds = 1 * 60; | |
| 77 | + private int localTimeToIdleSeconds = 0; | |
| 78 | + /** | |
| 79 | + * 本地缓存清理周期,单位秒,默认3分钟 | |
| 80 | + */ | |
| 81 | + private int localDiskExpiryThreadIntervalSeconds = 90; | |
| 82 | + } | |
| 83 | +} | ... | ... |
src/main/java/com/irrigation/icl/entity/PageList.java
| ... | ... | @@ -9,10 +9,14 @@ import lombok.NoArgsConstructor; |
| 9 | 9 | |
| 10 | 10 | import java.util.List; |
| 11 | 11 | |
| 12 | +/** | |
| 13 | + * 分页返回类 | |
| 14 | + * @param <E> | |
| 15 | + */ | |
| 12 | 16 | @Data |
| 13 | 17 | @NoArgsConstructor |
| 14 | 18 | @AllArgsConstructor |
| 15 | -@ApiModel(value = "分页返回类") | |
| 19 | +@ApiModel(value = "PageList") | |
| 16 | 20 | public class PageList<E> { |
| 17 | 21 | |
| 18 | 22 | @ApiModelProperty(value = "总页数") | ... | ... |
src/main/java/com/irrigation/icl/entity/PageParam.java
| ... | ... | @@ -6,10 +6,13 @@ import lombok.AllArgsConstructor; |
| 6 | 6 | import lombok.Data; |
| 7 | 7 | import lombok.NoArgsConstructor; |
| 8 | 8 | |
| 9 | +/** | |
| 10 | + * 分页参数类 | |
| 11 | + */ | |
| 9 | 12 | @Data |
| 10 | 13 | @NoArgsConstructor |
| 11 | 14 | @AllArgsConstructor |
| 12 | -@ApiModel(value = "分页参数类") | |
| 15 | +@ApiModel(value = "PageParam") | |
| 13 | 16 | public class PageParam { |
| 14 | 17 | |
| 15 | 18 | @ApiModelProperty(value = "当前页") | ... | ... |
src/main/java/com/irrigation/icl/entity/RestResult.java
| ... | ... | @@ -10,14 +10,18 @@ import lombok.AllArgsConstructor; |
| 10 | 10 | import lombok.Data; |
| 11 | 11 | import lombok.NoArgsConstructor; |
| 12 | 12 | |
| 13 | +/** | |
| 14 | + * 公共返回类 | |
| 15 | + * @param <T> | |
| 16 | + */ | |
| 13 | 17 | @Data |
| 14 | 18 | @AllArgsConstructor |
| 15 | 19 | @NoArgsConstructor |
| 16 | -@ApiModel(value = "公共返回类") | |
| 20 | +@ApiModel(value = "RestResult") | |
| 17 | 21 | public class RestResult<T> { |
| 18 | - @ApiModelProperty(value = "返回编码类型") | |
| 22 | + @ApiModelProperty(value = "返回编码类型",example = "200",allowableValues = "200,400") | |
| 19 | 23 | private int code; |
| 20 | - @ApiModelProperty(value = "返回提示消息") | |
| 24 | + @ApiModelProperty(value = "返回提示消息",example = "成功") | |
| 21 | 25 | private String message; |
| 22 | 26 | @ApiModelProperty(value = "返回请求数据对象") |
| 23 | 27 | private T data; | ... | ... |
src/main/java/com/irrigation/icl/enums/ResultEnum.java
src/main/java/com/irrigation/icl/utils/RestResultGeneratorUtil.java
| ... | ... | @@ -11,23 +11,35 @@ public class RestResultGeneratorUtil { |
| 11 | 11 | return result; |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | + public static <T> RestResult<T> getSuccessResult() { | |
| 15 | + return getResult(ResultEnum.SUCCESS.getCode(), null, ResultEnum.SUCCESS.getMessage()); | |
| 16 | + } | |
| 14 | 17 | public static <T> RestResult<T> getSuccessResult(T data) { |
| 15 | 18 | return getResult(ResultEnum.SUCCESS.getCode(), data, ResultEnum.SUCCESS.getMessage()); |
| 16 | 19 | } |
| 17 | 20 | |
| 18 | - public static RestResult<String> getSuccessResult() { | |
| 19 | - return getResult(ResultEnum.SUCCESS.getCode(), null, ResultEnum.SUCCESS.getMessage()); | |
| 21 | + public static <T> RestResult<T> getSuccessResult(T data,String message) { | |
| 22 | + return getResult(ResultEnum.SUCCESS.getCode(), data, message); | |
| 20 | 23 | } |
| 21 | 24 | |
| 22 | - public static RestResult<String> getErrorResult(String message) { | |
| 25 | + public static <T> RestResult<T> getSuccessResultMsg(String message) { | |
| 26 | + return getResult(ResultEnum.SUCCESS.getCode(), null, message); | |
| 27 | + } | |
| 28 | + | |
| 29 | + | |
| 30 | +// public static RestResult<String> getErrorResult(String message) { | |
| 31 | +// return getResult(ResultEnum.ERROR.getCode(), null, message); | |
| 32 | +// } | |
| 33 | + | |
| 34 | + public static <T> RestResult<T> getErrorResult(String message) { | |
| 23 | 35 | return getResult(ResultEnum.ERROR.getCode(), null, message); |
| 24 | 36 | } |
| 25 | 37 | |
| 26 | - public static RestResult<String> getErrorResult(int code, String message) { | |
| 38 | + public static <T> RestResult<T> getErrorResult(int code, String message) { | |
| 27 | 39 | return getResult(code, null, message); |
| 28 | 40 | } |
| 29 | 41 | |
| 30 | - public static RestResult<String> getErrorResult(ResultEnum resultEnum) { | |
| 42 | + public static <T> RestResult<T> getErrorResult(ResultEnum resultEnum) { | |
| 31 | 43 | return getResult(resultEnum.getCode(), null, resultEnum.getMessage()); |
| 32 | 44 | } |
| 33 | 45 | } | ... | ... |