|
|
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 |
...
|
...
|
|