当前位置: 首页 > 知识库问答 >
问题:

根据需要调用多少页来并行化do while循环?

池庆
2023-03-14

我必须通过传递header和body来发出HTTP POST请求。在正文中,我需要在发布数据之前提供pageNumber,所以我最初以“1”开头。之后,我将发布数据,并返回JSON响应,如下所示。

{
    "response": {
        "pageNumber": 1,
        "entries": 200,
        "numberOfPages": 3
    },
    "list": [
        {
            // some stuff here
        }
    ],
    "total": 1000
}

现在,根据pageNumber1的响应,我将决定还需要进行多少次调用。现在在上面的响应中,numberofpages是3,所以我需要对同一个URL总共进行三次调用。由于我们已经进行了1个调用,我将再进行2个调用,在主体中使用pageNumber“2”和“3”。

下面是我的工作代码。我只需要通过更改正文调用相同的URL直到numberofpages次。对于每个呼叫,都应该使用相应的PageNumber进行,因此如果NumberOfPages是3,那么我将总共进行3个呼叫。从每页收集数据后,我正在填充两张地图。

public class AppParser {
  private static final ObjectMapper objectMapper = new ObjectMapper();
  private static final String lastParentIdJsonPath = "......";    
  private final Map<String, String> processToTaskIdHolder = new HashMap<>();
  private final Multimap<String, Category> itemsByCategory = LinkedListMultimap.create();
  private final int entries;
  private final String siteId;

  public AppParser(int entries, String id) {
    this.entries = entries;
    this.id = id;
    collect();
  }

  // this is only called from above constructor
  private void collect() {
    String endpoint = "url_endpoint";
    int number = 1;
    int expectedNumber;
    do {
      HttpEntity<String> requestEntity = new HttpEntity<String>(getBody(number), getHeader());
      ResponseEntity<String> responseEntity =
          HttpClient.getInstance().getClient()
              .exchange(URI.create(endpoint), HttpMethod.POST, requestEntity, String.class);
      String jsonInput = responseEntity.getBody();
      Stuff response = objectMapper.readValue(jsonInput, Stuff.class);
      expectedNumber = (int) response.getPaginationResponse().getNumberOfPages();
      if (expectedNumber <= 0) {
        break;
      }
      List<Postings> postings = response.getPostings();
      for (Postings posting : postings) {
        if (posting.getClientIds().isEmpty()) {
          continue;
        }
        List<String> lastParent = JsonPath.read(jsonInput, lastParentIdJsonPath);
        String clientId = posting.getClientIds().get(0).getId();
        Category category = getCategory(posting);
        // populate two maps now
        itemsByCategory.put(clientId, category);
        processToTaskIdHolder.put(clientId, lastParent.get(0));
      }
      number++;
    } while (number <= expectedNumber);
  }

  private String getBody(final int number) {
    Input input = new Input(entries, number, 0);
    Body body = new Body("Stuff", input);
    return gson.toJson(body);
  }

  // getters for those two above maps
}

现在我的上面的代码是按顺序为每个页面一个一个地收集数据,所以如果我有很高的numberofpages,那么收集所有这些页码的所有数据将需要一些时间。假设numberofpages是500,那么我的代码将为每个PageNumber逐个依次运行。有没有什么方法可以并行化我上面的代码,这样我们就可以同时收集数据,比如说5页?这有可能做到吗?我想我需要确保我的代码是线程安全的。

注意:HttpClient是线程安全的单例类。

共有1个答案

满自明
2023-03-14

我试图用多线程修改您的代码,但这并不容易,因为您没有提供包含所有导入的完整类源代码。此外,您的代码也不像可能的那样干净。您的任务是异步请求的常见情况。我将您的收集代码包装到java.util.concurrent.callable中。它通过ExecutorService为我提供异步使用任务,并在需要时将结果作为parseresult对象获取。在下面的代码中,我提出了一个请求来填充expectedNumber变量,循环中应用程序创建任务并将它们提交到executorservice中,并使用它们运行的专用线程池。代码:

private static final ObjectMapper objectMapper = new ObjectMapper();
private static final String URL_ENDPOINT = "url_endpoint";
private final Map<String, String> processToTaskIdHolder = new HashMap<>();
private final Multimap<String, Category> itemsByCategory = LinkedListMultimap.create();
private static final String lastParentIdJsonPath = "......";

class ParseResult {
    private String clientId;
    private Category category;
    private String lastParent;
    private int expectedNumber;
}

class ParseTask implements Callable<ParseResult> {

    private int pageNumber;

    public ParseTask(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    @Override
    public ParseResult call() throws Exception {
        HttpEntity<String> requestEntity = new HttpEntity<String>(getBody(pageNumber), getHeader());
        ResponseEntity<String> responseEntity =
                HttpClient.getInstance().getClient()
                        .exchange(URI.create(URL_ENDPOINT), HttpMethod.POST, requestEntity, String.class);
        String jsonInput = responseEntity.getBody();
        Stuff response = objectMapper.readValue(jsonInput, Stuff.class);
        int expectedNumber = (int) response.getPaginationResponse().getNumberOfPages();
        if (expectedNumber <= 0) {
            return null; // or throw exception
        }
        List<Postings> postings = response.getPostings();
        for (Postings posting : postings) {
            if (posting.getClientIds().isEmpty()) {
                continue;
            }
            List<String> lastParent = JsonPath.read(jsonInput, lastParentIdJsonPath);
            String clientId = posting.getClientIds().get(0).getId();
            Category category = getCategory(posting);

            //collecting the result
            ParseResult parseResult = new ParseResult();
            parseResult.clientId = clientId;
            parseResult.category = category;
            parseResult.expectedNumber = expectedNumber;
            parseResult.lastParent = lastParent.get(0);
            writeResult(parseResult); // writing the result
            return parseResult;
        }
    }
}

public AppParser(int entries, String id) {
    // .....
    collect();
}

// this is only called from above constructor
private void collect() {
    int number = 1;
    int expectedNumber = 0;
    ParseTask parseTask = new ParseTask(number);
    try {
        ParseResult firstResult = parseTask.call();
        expectedNumber = firstResult.expectedNumber; // fill the pages amount
    } catch (Exception e) {
        e.printStackTrace();
    }

    ExecutorService executorService = Executors.newCachedThreadPool();
    while (number <= expectedNumber) {
        executorService.submit(new ParseTask(number));
    }
}

private String getBody(final int number) {
    Input input = new Input(entries, number, 0);
    Body body = new Body("Stuff", input);
    return gson.toJson(body);
}

private void writeResult(ParseResult result) {
    // populate two maps now
    itemsByCategory.put(result.clientId, result.category);
    processToTaskIdHolder.put(result.clientId, result.lastParent);
}

我们可以花很多时间来升级您的代码,但这是一个多线程的原始版本。我不确定它是否有效,因为正如我之前所说,你没有提供完整的版本。也许它需要一些语法修复。

 类似资料:
  • 问题内容: 有没有办法知道在Java中执行一个循环要花多少秒? 例如: 它不必精确到100%,而只是想知道要花费多长时间。里面的算法是一种写入.txt文件的密钥生成器。我希望它花费几分钟,因此对于我的第一次测试,我想计算秒数。 问题答案: 用Java编写微基准测试时需要非常小心。例如: 如果JIT编译器可以弄清楚循环体不影响代码结果,则可以对其进行优化。例如: 可能很快就会“运行”。 JIT编译后

  • 我正在尝试创建一个脚本,该脚本将循环txt文件中列出的所有服务,检查服务启动类型是否正确(如果不正确,则更改它),并在需要时启动服务。我不太擅长Powershell,也不会真正从谷歌找到任何有用的东西。 我的文本文件: 我当前的脚本看起来是这样的,目前我能够从文本文件中打印每一个服务,但缺少下一步的信息。 困难的是每个服务都不具有相同的启动类型和状态,因此它更加复杂,例如 服务A需要手动并正在运行

  • 问题内容: 我有以下声明: 我需要为role_id 101-355生成一行(因此,与上述相同的语句,除了随着role_id递增而重复)。最好的方法是什么?为了完成这项工作,我打算编写一个快速的C#应用​​程序,该应用程序将有一个循环,但是我确信这不是最好的方法,希望在这里学习一些东西,以免将来不得不这样做(因为我确保这种情况很常见)。 问题答案: 您应该使用数字表,如果没有数字表,可以这样使用:

  • 我将firebase添加到我的android项目中,以使用firebase云消息传递。我按照文档进行了操作,但没有找到调用的任何说明。 我的应用程序工作很好,除了有一次它崩溃了以下错误。 当我搜索错误时,给出的解决方案是在启动时调用。 我想知道这是否真的有必要,因为文档没有提到它,我的应用程序在没有它的情况下工作(大部分)很好。 有人知道调用是否真的有必要,还有什么可能导致我上面提到的错误吗? 下

  • http://localhost/test?name= 它的工作没有任何问题,没有抛出任何异常或任何其他迹象。为什么?

  • 编写一个程序,根据CSE 1341教学大纲计算当前成绩。程序应该提示用户输入他们的名字和姓氏。然后,它将这些名称作为字符串传递给CSE1341Grade类的第二个方法。第二种方法的名称为calcGrade。此方法将提示用户输入考试分数计数、测验分数计数和实验室分数计数。 然后,它将利用重复结构根据之前输入的计数提示考试成绩、测验成绩和实验室成绩。例如,如果用户输入的考试分数计数为2;然后程序将循环