Authored by LUOXC

资源位配置加缓存

package com.yohoufo.resource.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.CacheLoader;
import com.yoho.core.cache.LocalCache;
import com.yoho.core.redis.cluster.annotation.Redis;
import com.yoho.core.redis.cluster.operations.nosync.YHRedisTemplate;
import com.yoho.core.redis.cluster.operations.nosync.YHValueOperations;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
import com.yohoufo.dal.resource.ConfigTypeMapper;
import com.yohoufo.dal.resource.model.ConfigType;
import com.yohoufo.resource.service.IConfigTypeService;
import com.yohoufo.resource.util.KeySerializer;
import com.yohoufo.resource.util.RedisCacheBuilder;
import com.yohoufo.resource.util.RedisLoadingCache;
import com.yohoufo.resource.util.ValueSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -13,6 +23,7 @@ import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
... ... @@ -27,6 +38,48 @@ public class ConfigTypeServiceImpl implements IConfigTypeService {
private final String NormalConfigTypeList_CACHE_KEY = "NormalConfigTypeList";
@Redis("yohoNoSyncRedis")
private YHRedisTemplate redisTemplate;
@Redis("yohoNoSyncRedis")
private YHValueOperations valueOperations;
private final RedisLoadingCache<String, ConfigType> codeConfigTypeRedisCache = RedisCacheBuilder.newBuilder()
.withYhRedis(redisTemplate, valueOperations)
.withExpire(5, TimeUnit.SECONDS)
.withKeySerializer(new KeySerializer<String>() {
@Override
public RedisKeyBuilder serialize(String obj) {
return new RedisKeyBuilder().appendFixed("ufo:resources:configType:code:").appendVar(obj);
}
@Override
public String deserialize(RedisKeyBuilder keyBuilder) {
return keyBuilder.getKey().substring(keyBuilder.getKey().lastIndexOf("ufo:resources:configType:") + 1);
}
})
.withValueSerializer(new ValueSerializer<ConfigType>() {
@Override
public String serialize(ConfigType obj) {
return JSONObject.toJSONString(obj);
}
@Override
public ConfigType deserialize(String objectData) {
return JSONObject.parseObject(objectData, ConfigType.class);
}
})
.build(new CacheLoader<String, ConfigType>() {
@Override
public ConfigType load(String code) throws Exception {
return configTypeMapper.selectByCode(code);
}
});
@PostConstruct
private void init() {
localCache.init(NormalConfigTypeList_CACHE_KEY, 5, TimeUnit.MINUTES, (String s, Object o) -> {
... ... @@ -52,10 +105,15 @@ public class ConfigTypeServiceImpl implements IConfigTypeService {
@Override
public String getContentByCode(String code) {
return Optional.ofNullable(configTypeMapper.selectByCode(code))
.filter(e -> e.getStatus().intValue() == 1)
.map(ConfigType::getContent)
.orElse(null);
try {
return Optional.ofNullable(codeConfigTypeRedisCache.get(code))
.filter(e -> e.getStatus().intValue() == 1)
.map(ConfigType::getContent)
.orElse(null);
} catch (ExecutionException e) {
LOGGER.info("get content by code fail, code is {}", code, e);
return null;
}
}
@Override
... ... @@ -63,7 +121,9 @@ public class ConfigTypeServiceImpl implements IConfigTypeService {
ConfigType configType = new ConfigType();
configType.setCode(code);
configType.setContent(content);
return configTypeMapper.updateByCode(configType);
int rows = configTypeMapper.updateByCode(configType);
codeConfigTypeRedisCache.invalidate(code);
return rows;
}
}
... ...
package com.yohoufo.resource.util;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
public interface KeySerializer<T> {
RedisKeyBuilder serialize(final T obj);
T deserialize(final RedisKeyBuilder objectData);
}
... ...
package com.yohoufo.resource.util;
import com.google.common.cache.CacheLoader;
import com.yoho.core.redis.cluster.operations.nosync.YHRedisTemplate;
import com.yoho.core.redis.cluster.operations.nosync.YHValueOperations;
import java.util.concurrent.TimeUnit;
public class RedisCacheBuilder<K, V> {
private YHRedisTemplate redisTemplate;
private YHValueOperations valueOperations;
private long timeout;
private TimeUnit timeUnit;
KeySerializer<? extends K> keySerializer;
ValueSerializer<? extends V> valueSerializer;
public static RedisCacheBuilder<Object, Object> newBuilder() {
return new RedisCacheBuilder<>();
}
public RedisCacheBuilder<K, V> withYhRedis(YHRedisTemplate redisTemplate, YHValueOperations valueOperations) {
this.redisTemplate = redisTemplate;
this.valueOperations = valueOperations;
return this;
}
public RedisCacheBuilder<K, V> withExpire(long timeout, TimeUnit timeUnit) {
this.timeout = timeout;
this.timeUnit = timeUnit;
return this;
}
public <K1 extends K> RedisCacheBuilder<K, V> withKeySerializer(KeySerializer<K1> keySerializer) {
this.keySerializer = keySerializer;
return this;
}
public <V1 extends V> RedisCacheBuilder<K, V> withValueSerializer(ValueSerializer<V1> valueSerializer) {
this.valueSerializer = valueSerializer;
return this;
}
public <K1 extends K, V1 extends V> RedisLoadingCache<K1, V1> build(CacheLoader<K1, V1> loader) {
return new RedisLoadingCache<>(redisTemplate, valueOperations, keySerializer, valueSerializer, timeout, timeUnit, loader);
}
}
... ...
package com.yohoufo.resource.util;
import com.google.common.cache.AbstractLoadingCache;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ExecutionError;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.yoho.core.redis.cluster.operations.nosync.YHRedisTemplate;
import com.yoho.core.redis.cluster.operations.nosync.YHValueOperations;
import com.yoho.core.redis.cluster.operations.serializer.RedisKeyBuilder;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@Slf4j
public class RedisLoadingCache<K, V> extends AbstractLoadingCache<K, V> implements LoadingCache<K, V> {
private final YHRedisTemplate redisTemplate;
private final YHValueOperations valueOperations;
private final KeySerializer<K> keySerializer;
private final ValueSerializer<V> valueSerializer;
private final long timeout;
private final TimeUnit unit;
private final CacheLoader<K, V> loader;
RedisLoadingCache(
YHRedisTemplate redisTemplate,
YHValueOperations valueOperations,
KeySerializer keySerializer,
ValueSerializer valueSerializer,
long timeout,
TimeUnit unit,
CacheLoader<K, V> loader) {
this.redisTemplate = redisTemplate;
this.valueOperations = valueOperations;
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.timeout = timeout;
this.unit = unit;
this.loader = loader;
}
@Override
public V getIfPresent(Object o) {
RedisKeyBuilder key = keySerializer.serialize((K) o);
String value;
try {
value = valueOperations.get(key);
} catch (Exception e) {
log.warn("redis get fail, key {}", key.getKey());
value = null;
}
if (value == null) {
return null;
} else {
return valueSerializer.deserialize(value);
}
}
@Override
public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException {
V value = this.getIfPresent(key);
if (value == null) {
try {
value = valueLoader.call();
} catch (Exception e) {
convertAndThrow(e);
}
if (value != null) {
this.put(key, value);
}
}
return value;
}
@Override
public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) {
List<RedisKeyBuilder> keyBuilders = serializeKeys(keys);
List<String> values;
try {
values = valueOperations.multiGet(keyBuilders);
} catch (Exception e) {
log.warn("redis mget fail", e);
values = null;
}
Map<K, V> map = new LinkedHashMap<>();
int i = 0;
for (Object key : keys) {
K castKey = (K) key;
if (Objects.nonNull(values) && values.get(i) != null) {
map.put(castKey, valueSerializer.deserialize(values.get(i)));
}
i++;
}
return ImmutableMap.copyOf(map);
}
private List<RedisKeyBuilder> serializeKeys(Iterable<?> keys) {
List<RedisKeyBuilder> keyBuilders = new ArrayList<>();
for (Object key : keys) {
keyBuilders.add(keySerializer.serialize((K) key));
}
return keyBuilders;
}
@Override
public void put(K key, V value) {
RedisKeyBuilder keyBuilder = keySerializer.serialize(key);
String valueBytes = valueSerializer.serialize(value);
try {
valueOperations.set(keyBuilder, valueBytes, timeout, unit);
} catch (Exception e) {
log.warn("redis set fail, key {}", keyBuilder.getKey());
// ignore
}
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
Map<RedisKeyBuilder, String> mm = Maps.newHashMap();
m.forEach((key, value) -> mm.put(keySerializer.serialize(key), valueSerializer.serialize(value)));
try {
valueOperations.multiSet(mm);
} catch (Exception e) {
log.warn("redis mset fail", e);
// ignore
}
try {
mm.forEach((key, value) -> redisTemplate.expire(key, timeout, unit));
} catch (Exception e) {
// try again
try {
mm.forEach((key, value) -> redisTemplate.expire(key, timeout, unit));
} catch (Exception e1) {
// ignore
log.warn("redis expire fail", e1);
}
}
}
@Override
public void invalidate(Object key) {
try {
redisTemplate.delete(keySerializer.serialize((K) key));
} catch (Exception e) {
// try again
try {
redisTemplate.delete(keySerializer.serialize((K) key));
} catch (Exception e1) {
// ignore
log.warn("redis delete fail", e1);
}
}
}
@Override
public void invalidateAll(Iterable<?> keys) {
List<RedisKeyBuilder> keyBuilders = serializeKeys(keys);
try {
redisTemplate.delete(keyBuilders);
} catch (Exception e) {
// try again
try {
redisTemplate.delete(keyBuilders);
} catch (Exception e1) {
// ignore
log.warn("redis delete fail", e1);
}
}
}
@Override
public V get(final K key) throws ExecutionException {
return this.get(key, () -> loader.load(key));
}
@Override
public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
Map<K, V> result = Maps.newLinkedHashMap(this.getAllPresent(keys));
Set<K> keysToLoad = Sets.newLinkedHashSet(keys);
keysToLoad.removeAll(result.keySet());
if (!keysToLoad.isEmpty()) {
Map<K, V> newEntries = null;
try {
newEntries = loader.loadAll(keysToLoad);
} catch (Exception e) {
convertAndThrow(e);
}
if (newEntries != null) {
this.putAll(newEntries);
for (K key : keysToLoad) {
V value = newEntries.get(key);
if (value != null) {
result.put(key, value);
}
}
}
}
return ImmutableMap.copyOf(result);
}
@Override
public void refresh(K key) {
try {
V value = loader.load(key);
this.put(key, value);
} catch (Exception e) {
log.warn("Exception thrown during refresh", e);
}
}
private static void convertAndThrow(Throwable t) throws ExecutionException {
if (t instanceof InterruptedException) {
Thread.currentThread().interrupt();
throw new ExecutionException(t);
} else if (t instanceof RuntimeException) {
throw new UncheckedExecutionException(t);
} else if (t instanceof Exception) {
throw new ExecutionException(t);
} else {
throw new ExecutionError((Error) t);
}
}
}
\ No newline at end of file
... ...
package com.yohoufo.resource.util;
public interface ValueSerializer<T> {
String serialize(final T obj);
T deserialize(final String objectData);
}
... ...