代码之家  ›  专栏  ›  技术社区  ›  Hleb

提高与get方法调用并行的批量HTTP REST调用的性能

  •  2
  • Hleb  · 技术社区  · 7 年前

    在我开发的应用程序中,我需要执行大量的REST调用。我需要交互的RESTAPI资源的体系结构是分层的,看起来像:

    /api/continents - return list of all Earth's continents
    /api/continents/{continent_name}/countries - return list of all countries on mentioned continent
    /api/continents/{continent_name}/countries/{country_name}/cities - return list of all cities in mentioned country
    

    不幸的是,这个API没有提供任何方法来获取所有的城市,我需要首先获取所有大陆的列表,然后获取每个大陆的所有国家的列表,然后获取每个大陆每个国家的所有城市的列表。

    首先,我尝试实现我的方法,从该API中获取所有城市,而不需要只使用连续调用进行并行化。就像这样:

    private List<City> getCities() {
        List<Continent> continents = getAllContinents(); //HTTP GET call
        List<Country> countries = new ArrayList<>();
        for (Continent continent: continents) {
            countries.addAll(getAllCountriesOfContinent(continent));
        }
        List<City> cities = new ArrayList<>();
        for (Country country : countries) {
            cities.addAll(getAllCitiesOfCountry(country));
        }
        return cities;
    }
    

    但这种方法工作得太慢(具体数字是7小时左右)。我决定尝试使用Java并行流和完整的未来来改进它,并获得了这样的方法:

    private List<City> getCities() {
        return getAllContinents()
            .parallelStream()
            .map(continent -> getAllCountriesOfContinent(continent))
            .flatMap(feature -> feature.join().parallelStream())
            .map(country -> getAllCitiesOfCountry(country))
            .flatMap(feature -> feature.join().parallelStream())
            .collect(Collectors.toList());
    }
    

    其中,getAllCountriesOfContinentand getAllCitiesOfCountry方法返回了复杂未来的列表,如下所示:

    private CompletableFuture<List<Country>> getAllCountriesOfContinent(Continent continent) {
        return CompletableFuture.supplyAsync(() -> {
            return restClient.getDataFromApi(continent);
        });
    }
    
    private CompletableFuture<List<City>> getAllCitiesOfCountry(Country country) {
        return CompletableFuture.supplyAsync(() -> {
            return restClient.getDataFromApi(country);
        });
    }
    

    通过这样的重构,我得到了很好的性能提升(它执行了大约25-30分钟)。但我认为我可以通过使用Java TycPoLeExcel和线程或FokCuin框架来改进它。这些方法是否会帮助我提高代码的性能,或者有其他一些特殊的技术/算法/框架?

    2 回复  |  直到 7 年前
        1
  •  2
  •   GhostCat    7 年前

    这些方法会帮助我提高性能吗?

    答案是:可能。

    你看, parallelStream() 为您提供多线程的“默认”实现(在封面下,此操作实际上使用了forkjoin框架)。

    换句话说:你可以退后一步,花很多时间做实验,在那里你使用不同的低级方法,并测量相应的结果。是的,最有可能的是,当你花1周时间微调你的算法时,你 应该 最终可以得到比依赖Java提供的“默认实现”更好的东西。

    但是,你得到了多少改进,你需要多长时间才能达到,这是很难预测的。

    因此,真正的答案是:

    • 测量哪项操作需要多长时间,以确定您的真正瓶颈在您的 总体的 系统(例如:典型的客户机应该使用 每个国家的线程数,获取这些城市的线程数,或者更小的线程数会更有帮助)
    • 如果可能的话,增强RESTAPI,只需给你一个城市列表就可以了

    长话短说:你必须权衡一下。您可以编写许多自定义代码以获得更好的结果。但是没有人能预先告诉你你将获得的收益,以及多少“成本”将添加到你的“预算”中,因为“随着时间的推移编写和维护更复杂的代码”。

        2
  •  1
  •   Molly Stewart-Gallus    7 年前

    我觉得在这里多线程并不是一个正确的工具,因为这是一个网络通信问题,而不是一个计算问题。

    特别是因为Java缺少协同程序并行流可能是一个好的和合理的选择,以便在飞行中同时管理多个HTTP请求,但它并不是真正要解决的最重要的一点。

    你应该关注的是网络细节,而不是CPU细节。这种情况尤其让我想起了HTTP/2,它应该允许多个这样的请求同时运行。您还应该研究HTTP管道,它在早期版本上受支持,但安装起来要复杂得多。

    推荐文章