Как узнать, что все асинхронные HTTP-вызовы завершены - PullRequest
1 голос
/ 25 апреля 2019

Я пытаюсь выяснить, все ли выполненные асинхронные HTTP-запросы GET выполнены, чтобы я мог выполнить другой метод.Для контекста у меня есть что-то похожее на приведенный ниже код:

public void init() throws IOException {
    Map<String, CustomObject> mapOfObjects = new HashMap<String, CustomObject>();
    ObjectMapper mapper = new ObjectMapper();

    // some code to populate the map

    mapOfObjects.forEach((k,v) -> {
        HttpClient.asyncGet("https://fakeurl1.com/item/" + k, createCustomCallbackOne(k, mapper)); 
        // HttpClient is just a wrapper class for your standard OkHTTP3 calls, 
        // e.g. client.newcall(request).enqueue(callback);
        HttpClient.asyncGet("https://fakeurl2.com/item/" + k, createCustomCallbackTwo(k, mapper));
    });
}


private createCustomCallbackOne(String id, ObjectMapper mapper) {
    return new Callback() {
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if (response.isSuccessful()) {
                try (ResponseBody body = response.body()) {
                    CustomObject co = mapOfObjects.get(id);
                    if (co != null) {
                        co.setFieldOne(mapper.readValue(body.byteStream(), FieldOne.class))); 
                    }                       
                } // implicitly closes the response body                
            }    
        }

        @Override
        public void onFailure(Call call, IOException e) {
            // log error                
        }
    }
}

// createCustomCallbackTwo does more or less the same thing, 
// just sets a different field and then performs another 
// async GET in order to set an additional field

Итак, что будет лучшим / правильным способом контроля всех этих асинхронных вызовов, чтобы убедиться, что они завершены, и я могу приступить к выполнению другого метода наОбъекты, хранящиеся внутри карты?

Ответы [ 2 ]

2 голосов
/ 25 апреля 2019

Самый простой способ - подсчитать, сколько запросов «в полете».Увеличивайте его для каждого поставленного в очередь запроса, уменьшайте его в конце обратного вызова.Когда / если число равно 0, любые / все запросы выполняются.Используя семафор или блокировку подсчета, вы можете wait, чтобы он стал 0 без опроса.

Обратите внимание, что обратные вызовы выполняются в отдельных потоках, поэтому вы должны обеспечить некоторую синхронизацию.

ЕслиВы хотите создать новый обратный вызов для каждого запроса, вы можете использовать что-то вроде этого:

public class WaitableCallback implements Callback {

  private boolean done;
  private IOException exception;

  private final Object[] signal = new Object[0];

  @Override
  public void onResponse(Call call, Response response) throws IOException {
      ...
      synchronized (this.signal) {
        done = true;
        signal.notifyAll();
      }
  }

  @Override
  public void onFailure(Call call, IOException e) {
    synchronized (signal) {
      done = true;
      exception = e;
      signal.notifyAll();
    }
  }

  public void waitUntilDone() throws InterruptedException {
    synchronized (this.signal) {
      while (!this.done) {
        this.signal.wait();
      }
    }
  }

  public boolean isDone() {
    synchronized (this.signal) {
      return this.done;
    }
  }

  public IOException getException() {
    synchronized (this.signal) {
      return exception;
    }
  }

}

Создать экземпляр для каждого запроса и поместить его, например, в List<WaitableCallback> pendingRequests.

Затемвы можете просто подождать выполнения всех запросов:

for ( WaitableCallback cb : pendingRequests ) {
  cb.waitUntilDone();
}
// At this point, all requests have been processed.

Однако вам, вероятно, не следует создавать новый идентичный объект обратного вызова для каждого запроса.Методы обратного вызова передают Call в качестве параметра, чтобы код мог проверить его, чтобы выяснить, какой запрос он обрабатывает;и в вашем случае, кажется, вам это даже не нужно.Поэтому используйте один экземпляр Callback для запросов, которые должны обрабатываться одинаково.

1 голос
/ 25 апреля 2019

Если функция asyncGet вызывает вашу функцию createCustomCallbackOne, тогда это просто.

Для каждой клавиши вы вызываете две страницы. «https://fakeurl1.com/item/" и» https://fakeurl2.com/item/" (пропущено + k)

Так что вам нужна карта, чтобы отследить это, и достаточно только одной функции обратного вызова.

Используйте карту с ключом, обозначающим каждый звонок:

  static final  Map<String, Integer> trackerOfAsyncCalls = new HashMap<>();
  public void init() throws IOException {
    Map<String, CustomObject> mapOfObjects = new HashMap<String, CustomObject>();
//need to keep a track of the keys in some object
ObjectMapper mapper = new ObjectMapper();
 trackerOfAsyncCalls.clear();
// some code to populate the map

mapOfObjects.forEach((k,v) -> {
    HttpClient.asyncGet("https://fakeurl1.com/item/" + k, createCustomCallback(k,1 , mapper)); 

    // HttpClient is just a wrapper class for your standard OkHTTP3 calls, 
    // e.g. client.newcall(request).enqueue(callback);
    HttpClient.asyncGet("https://fakeurl2.com/item/" + k, createCustomCallback(k, 2, mapper));
           trackerOfAsyncCalls.put(k + "-2", null);
        });
}

// последний важный private createCustomCallbackOne (конечная строка idOuter, int which, преобразователь ObjectMapper) { вернуть новый Callback () { final String myId = idOuter + "-" + which;

    trackerOfAsyncCalls.put(myId, null);
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
             trackerOfAsyncCalls.put(myId, 1);
            ///or put outside of if if u dont care if success or fail or partial...

Теперь настройте поток или, что лучше, schduler, который будет вызываться каждые 5 секунд, проверьте все eys в mapOfObjects и trackerOfAsyncCalls, чтобы увидеть, все ли ключи были запущены, и был ли достигнут какой-либо окончательный успех, время ожидания или состояние ошибки для всех.

...