有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在番石榴缓存中存储地图

我有一个Map<Range<Double>, String>检查特定Double值(分数)映射到String(级别)的位置。最终用户希望能够动态更改此映射,从长远来看,我们希望有一个基于web的GUI,他们可以控制此映射,但从短期来看,他们很高兴文件位于S3中,并在需要更改时对其进行编辑。我不想为每个请求点击S3,而是想缓存它,因为它不会太频繁地更改(大约一周一次)。我也不想改变代码,取消我的服务

以下是我的想法-

public class Mapper() {
    private LoadingCache<Score, String> scoreToLevelCache;

public Mapper() {
    scoreToLevelCache = CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build(new CacheLoader<Score, String>() {
                public String load(Score score) {
                    Map<Range<Double>, String> scoreToLevelMap = readMappingFromS3(); //readMappingFromS3 omitted for brevity
                    for(Range<Double> key : scoreToLevelMap.keySet()) {
                        if(key.contains(score.getCount())) { return scoreToLevelMap.get(key); }
                    }
                    throw new IllegalArgumentException("The score couldn't be mapped to a level. Either the score passed in was incorrect or the mapping is incorrect");
                }
            }); 
}

public String getContentLevelForScore(Score Score) {
    try {
        return scoreToLevelCache.get(Score);
    } catch (ExecutionException e) { throw new InternalServerException(e); }
  } 
}

这种方法的明显问题在于load方法 Map<Range<Double>, String> scoreToLevelMap = readMappingFromS3(); 对于每个键,我都会一遍又一遍地加载整个地图。这不是一个性能问题,但当规模增加时,它可能会成为一个问题,无论如何,这不是一个有效的方法

我认为把整个地图保存在缓存中会更好,但我不知道该怎么做。有人能帮上忙吗,或者提出一种更优雅的方法来实现这一点


共 (2) 个答案

  1. # 1 楼答案

    番石榴对“只包含一个值的缓存”有不同的机制;它叫^{}

    private Supplier<Map<Range<Double>, String> cachedMap = 
        Suppliers.memoizeWithExpiration(
            new Supplier<Map<Range<Double>, String>() {
                public Map<Range<Double>, String> get() {
                    return readMappingFromS3();
                }
            }, 10, TimeUnit.MINUTES);
    
    public String getContentLevelForScore(Score score) {
        Map<Range<Double>, String> scoreMap = cachedMap.get();
        // etc.
    }
    
  2. # 2 楼答案

    这里有一个解决方案,可以一次读取整个地图,并通过asyncReloading保持refreshing it asynchronouzly as needed

    它将在refresh期间返回旧值,而不会阻塞多个读卡器线程,比如Suppliers.memoizeWithExpirationdoes

    private static final Object DUMMY_KEY = new Object();
    private static final LoadingCache<Object, Map<Range<Double>, String>> scoreToLevelCache = 
            CacheBuilder.newBuilder()
            .maximumSize(1)
            .refreshAfterWrite(10, TimeUnit.MINUTES)
            .build(CacheLoader.asyncReloading(
                    new CacheLoader<Object, Map<Range<Double>, String>>() {
                        public Map<Range<Double>, String> load(Object key) {
                            return readMappingFromS3();
                        }
                    },
                    Executors.newSingleThreadExecutor()));
    
    public String getContentLevelForScore(Score score) {
        try {
            Map<Range<Double>, String> scoreMap = scoreToLevelCache.get(DUMMY_KEY);
            // traverse scoreMap ranges
            return level;
        } catch (ExecutionException e) {
            Throwables.throwIfUnchecked(e);
            throw new IllegalStateException(e);
        }
    }
    

    还考虑用RangeMap^ {CD3>}替换^ {< CD2>}以执行有效的远程查找。