Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java和/或Spring引导中的并发异步HTTP请求_Java_Spring Boot - Fatal编程技术网

Java和/或Spring引导中的并发异步HTTP请求

Java和/或Spring引导中的并发异步HTTP请求,java,spring-boot,Java,Spring Boot,我需要(用Java)解决的问题是: 从API获取游戏的数组 在游戏上迭代N次: (异步/并发) 为每个游戏发出一个单独的HTTP请求,以获取其 细节 将其详细信息存储在gamesWithDetails数组中 完成了。我有我的数组gamesWithDetails 我无法通过一个请求获取所有游戏的详细信息,每次游戏我都必须点击API端点。因此,我希望彼此异步执行这些请求 这是JavaScript中的一个工作示例,如果有用的话。然而,我想让它的工作为春季开机 axios.get(`https:/

我需要(用Java)解决的问题是:

  • 从API获取
    游戏的数组
    
  • 游戏上迭代
    N次:
    • (异步/并发)
    • 为每个游戏发出一个单独的HTTP请求,以获取其 细节
    • 将其详细信息存储在
      gamesWithDetails
      数组中
  • 完成了。我有我的数组
    gamesWithDetails
  • 我无法通过一个请求获取所有游戏的详细信息,每次游戏我都必须点击API端点。因此,我希望彼此异步执行这些请求

    这是JavaScript中的一个工作示例,如果有用的话。然而,我想让它的工作为春季开机

    axios.get(`https://la2.api.riotgames.com/lol/match/v4/matchlists/by-account/${data.accountId}`, {
      headers: { "X-Riot-Token": "asdasdasdasdadasdasdasd"}
    })
      .then(resp => {
        const promises = [];
    
        for ( match of resp.data.matches ) {
          promises.push(
            axios.get(`https://la2.api.riotgames.com/lol/match/v4/matches/${match.gameId}`, {
              headers: { "X-Riot-Token": "asdasdasdasdasdasdasdasd"}
            })
          )
        }
    
        Promise.all(promises)
          .then(matchesDetails => {
            matchesDetails.forEach(({ data }) => console.log(data.gameId));
          });
    
      })
    

    基本上你会想做这样的事情:

    package com.example.demo;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    import java.util.stream.Collectors;
    
    public class GamesProcessor {
        private static final String GAME_URI_BASE = "https://la2.api.riotgames.com/lol/match/v4/matches/";
        private static final String ACCOUNT_URI_BASE = "https://la2.api.riotgames.com/lol/match/v4/matchlists/by-account/";
        private Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() - 1);
    
        @Autowired
        private RestTemplate restTemplate;
    
        public void processGames(String accountId) throws JsonProcessingException, ExecutionException, InterruptedException {
            String responseAsString = restTemplate.getForObject(ACCOUNT_URI_BASE + accountId, String.class);
            ObjectMapper objectMapper = new ObjectMapper();
    
            if (responseAsString != null) {
                Map<String, Object> response = objectMapper.readValue(responseAsString, new TypeReference<Map<String, Object>>() {
                });
    
                List<Map<String, Object>> matches = (List<Map<String, Object>>) ((Map<String, Object>) response.get("data")).get("matches");
    
                List<CompletableFuture<Void>> futures = matches.stream()
                        .map(m -> (String) m.get("gameId"))
                        .map(gameId -> CompletableFuture.supplyAsync(() -> restTemplate.getForObject(GAME_URI_BASE + gameId, String.class), executor)
                                .thenAccept(r -> {
                                    System.out.println(r); //do whatever you wish with the response here
                                }))
                        .collect(Collectors.toList());
    
                // now we execute all requests asynchronously
                CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
            }
        }
    }
    
    
    这将执行第一个REST请求,并将其作为字符串(JSON响应)获取。您将希望使用Bean对象来正确地映射它。然后使用Jackson提供的ObjectMapper对其进行处理,并将其转换为一个映射,以便您可以导航JSON并获得匹配项

    List<CompletableFuture<Void>> futures = matches.stream()
                        .map(m -> (String) m.get("gameId"))
                        .map(gameId -> CompletableFuture.supplyAsync(() -> restTemplate.getForObject(GAME_URI_BASE + gameId, String.class), executor)
                                .thenAccept(r -> {
                                    System.out.println(r); //do whatever you wish with the response here
                                }))
                        .collect(Collectors.toList());
    
    这将针对每个matchId得到的每个响应执行,就像在您的示例中一样。这也应该被一个与输出相匹配的适当bean所代替,以便进行更清晰的处理


    请注意,
    List futures
    仅“保存代码”,但在我们最终使用
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]).get()组合所有内容之前,不会执行它并执行阻塞
    get()
    方法。

    这是一个非常有趣的问题,因为JavaScript实现了著名的方法,这意味着它的函数是异步的和非阻塞的。Spring Boot
    restTemplate
    类将阻塞执行线程,直到响应返回,因此会浪费大量资源(每个请求模型一个线程)

    @Slacky的回答在技术上是正确的,正如您所问的异步HTTP请求,但我想分享一个更好的选择,即异步和非阻塞,这意味着单个线程能够处理100甚至1000个请求及其响应(反应式编程

    在springboot中实现与JavaScript示例等效的方法是使用projectreactor
    WebClient
    类,它是一个非阻塞、反应式客户端,用于执行HTTP请求

    还值得一提的是,静态类型化的Java要求您声明类来表示数据,在本例中类似(为了简洁起见,使用Lombok):

    以下代码遵循@Slacky的答案命名约定,以便于比较

    public class GamesProcessor {
        private static final String BASE_URL = "https://la2.api.riotgames.com";
        private static final String GAME_URI = "/lol/match/v4/matches/%s";
        private static final String ACCOUNT_URI = "/lol/match/v4/matchlists/by-account/%s";
    
        public static List<MatchDetails> processGames(String accountId) { 
            final WebClient webClient = WebClient
                .builder()
                .baseUrl(BASE_URL)
                .defaultHeader("X-Riot-Token", "asdasdasdasdadasdasdasd") 
                .build();   
    
            // Issues the first request to get list of matches
            List<Match> matches = webClient
                .get()
                .uri(String.format(ACCOUNT_URI, accountId))
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(new ParameterizedTypeReference<List<Match>>() {})
                .block(); // blocks to wait for response
    
            // Processes the list of matches asynchronously and collect all responses in a list of matches details
            return Flux.fromIterable(matches)
                .flatMap(match -> webClient
                        .get()
                        .uri(String.format(GAME_URI, match.getGameId()))
                        .accept(MediaType.APPLICATION_JSON)
                        .retrieve()
                        .bodyToMono(MatchDetails.class))
                .collectList()
                .block();  // Blocks to wait for all responses
        }
    }
    
    公共类游戏处理器{
    私有静态最终字符串BASE_URL=”https://la2.api.riotgames.com";
    私有静态最终字符串GAME_URI=“/lol/match/v4/matches/%s”;
    私有静态最终字符串ACCOUNT_URI=“/lol/match/v4/matchlist/by ACCOUNT/%s”;
    公共静态列表processGames(字符串accountId){
    最终网络客户端网络客户端=网络客户端
    .builder()
    .baseUrl(基本URL)
    .defaultHeader(“X-Riot-Token”、“asdasdasd”)
    .build();
    //发出第一个请求以获取匹配项列表
    列表匹配项=webClient
    .get()
    .uri(String.format(ACCOUNT_uri,accountId))
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .BodyToNo(新的参数化类型引用(){})
    .block();//等待响应的块
    //异步处理匹配列表并收集匹配详细信息列表中的所有响应
    返回通量.fromIterable(匹配项)
    .flatMap(匹配->网络客户端)
    .get()
    .uri(String.format(GAME_uri,match.getGameId()))
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .BodyToNo(匹配详细信息.class))
    .LIST()
    .block();//等待所有响应的块
    }
    }
    
    System.out.println(r);
    
    @Data
    class Match {
        private String gameId;
        // ...
    }
    
    @Data
    class MatchDetails {
        // ...
    }
    
    public class GamesProcessor {
        private static final String BASE_URL = "https://la2.api.riotgames.com";
        private static final String GAME_URI = "/lol/match/v4/matches/%s";
        private static final String ACCOUNT_URI = "/lol/match/v4/matchlists/by-account/%s";
    
        public static List<MatchDetails> processGames(String accountId) { 
            final WebClient webClient = WebClient
                .builder()
                .baseUrl(BASE_URL)
                .defaultHeader("X-Riot-Token", "asdasdasdasdadasdasdasd") 
                .build();   
    
            // Issues the first request to get list of matches
            List<Match> matches = webClient
                .get()
                .uri(String.format(ACCOUNT_URI, accountId))
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(new ParameterizedTypeReference<List<Match>>() {})
                .block(); // blocks to wait for response
    
            // Processes the list of matches asynchronously and collect all responses in a list of matches details
            return Flux.fromIterable(matches)
                .flatMap(match -> webClient
                        .get()
                        .uri(String.format(GAME_URI, match.getGameId()))
                        .accept(MediaType.APPLICATION_JSON)
                        .retrieve()
                        .bodyToMono(MatchDetails.class))
                .collectList()
                .block();  // Blocks to wait for all responses
        }
    }