Java REST优化数据结构访问

时间:2019-01-20 19:02:27

标签: java multithreading concurrency

我有一个Java REST应用程序,其中一个端点始终处理=。我正在进行负载测试,当负载测试开始增加时真的很糟糕。

我可以实施哪些策略来提高应用程序的效率?

我应该使用Jetty线程吗,因为它是我正在使用的服务器?还是主要是代码?还是两者都有?

成为瓶颈的方法如下。

基本上,我需要从给定文件中读取一些行。我无法将其存储在数据库中,因此我通过Map进行了此处理。但是,我知道对于大文件,不仅要花很长时间才能上线,而且我冒这样一个事实,那就是Map包含很多条目时会占用大量内存...

ConcurrentMapdict

ConcurrentMap

2 个答案:

答案 0 :(得分:1)

ConcurrentMapsynchronized(this)混合可能不是正确的方法。 java.util.concurrent包中的类是为特定用例设计的,并尝试在内部优化同步。

相反,我建议首先尝试一个设计良好的缓存库,看看性能是否足够好。一个示例是Caffeine。根据{{​​3}}文档,它为您提供了一种声明如何甚至异步加载数据的方法:

AsyncLoadingCache<Key, Graph> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    // Either: Build with a synchronous computation that is wrapped as asynchronous 
    .buildAsync(key -> createExpensiveGraph(key));
    // Or: Build with a asynchronous computation that returns a future
    .buildAsync((key, executor) -> createExpensiveGraphAsync(key, executor));

答案 1 :(得分:1)

此解决方案基于ConcurrentHashMap#computeIfAbsent,有两个假设:

  1. 读取同一文件的多个线程不是问题。
  2. 尽管文档说由于阻塞而使计算应该简单而简短,但我相信这只是相同键(或存储桶/条带)访问的问题,而仅是更新(不是读取)的问题?在这种情况下,这不是问题,因为我们可以成功计算值或抛出IllegalArgumentException

使用此方法,通过将其作为放置密钥所需的计算,我们每个密钥仅打开一次文件。

    public String getLine(int lineNr) throws IllegalArgumentException {
        if (lineNr > nrLines) {
            throw new IllegalArgumentException();
        }
        return cache.computeIfAbsent(lineNr, (l) -> {
            try (Stream<String> st = Files.lines(path)) {
                Optional<String> optionalLine = st.skip(lineNr - 1).findFirst();
                if (optionalLine.isPresent()) {
                    return optionalLine.get();
                } else {
                    nrLines = nrLines > lineNr ? lineNr : nrLines;
                    throw new IllegalArgumentException();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        });
    }

我通过产生3个线程来“验证”第二个假设,其中:

  1. 线程1通过无限循环(永远阻止)来计算密钥0。
  2. 线程2尝试将键0放到位,但是因为线程1阻塞而从不这样做。
  3. 线程3尝试放入密钥1,并立即这样做。

尝试一下,也许能奏效,或者假设是错误的,那就太糟糕了。地图内部使用存储桶,因此即使锁定了不同的键,计算也可能成为瓶颈,因为它会锁定存储桶/条带。