Мое приложение обрабатывает много транзакций HTTP-запросов / ответов, где ответ от одной службы ведет к следующему шагу и последующему запросу и т. Д. По последовательности шагов.
Чтобы сделатькод элегантный, я использую структуру загрузки и обратного вызова, которую можно просто представить следующим образом:
private void runFirstStep() {
String firstRequest = buildRequest();
sendHttpRequest(firstRequest, this::handleFirstStepResponse);
}
private void handleFirstStepResponse(InputStream responseBody) {
doStuffWithFirstStepResponse(responseBody);
String secondRequest = buildSecondRequest();
sendHttpRequest(secondRequest, this::handleSecondStepResponse);
}
private void handleSecondStepResponse(InputStream responseBody) {
doStuffWithSecondStepResponse(responseBody);
String thirdRequest = buildThirdRequest();
sendHttpRequest(thirdRequest, this::handleThirdStepResponse);
}
private void handleThirdStepResponse(InputStream responseBody) {
doStuffWithThirdStepResponse(responseBody);
// The flow has finished, so no further HTTP transactions.
}
Хотя в моем случае длина последовательности в настоящее время достигает около 26 шагов, все соединены таким образом.
Это работает нормально, но я случайно заметил, что в консоли регистрируются строки журналирования, которые совершенно ясно дали понять, что каждый метод просто находится в ожидании завершения всех других методов в цепочке (что очевидно, когдаЯ думаю об этом).Но это также заставило меня подумать, что этот шаблон, который я использую, может подвергнуться риску переполнения стека.
Так что вопросы таковы:
Будет ли последовательностьнапример, из нескольких десятков связанных цепочек существует риск переполнения стека, или требуется намного больше злоупотреблений, чем это, чтобы исчерпать типичный стек?Обратите внимание, что моя упрощенная структура кода выше скрывает тот факт, что методы на самом деле делают довольно много (сборка XML, извлечение XML, запись данных запроса / ответа в файл журнала), поэтому мы не говорим об упрощенных задачах.
Есть ли другой шаблон, который я должен использовать, который не оставит цепочку методов, терпеливо ожидающих завершения всего потока?Мой метод sendHttpRequest
уже использует инфраструктуру HTTP JDK 11 для генерации CompletableFuture<HttpResponse<InputStream>>
, но мой метод sendHttpRequest
затем просто ожидает его завершения и вызывает указанный метод обратного вызова с результатом.Нужно ли создавать новый поток вместо этого для обработки CompletableFuture
, чтобы вызывающий метод мог корректно закрыться?И как сделать это, не вызывая JVM для выключения (если рассматривать медленный HTTP-ответ, то в результате JVM не будет выполнять методы)?
переполнение стека (сайт,не исключение) Я, конечно, ищу ответы, которые касаются механики голого металла Java, а не домыслов или анекдотов.
Обновление : просто чтобы уточнить, мой метод sendHttpRequest
в настоящее время имеет такую форму:
private void sendHttpRequest(String request,
Consumer<InputStream> callback) {
HttpRequest httpRequest = buildHttpRequestFromXml(request);
CompletableFuture<HttpResponse<InputStream>> completableExchange
= httpClient.
sendAsync(httpRequest, BodyHandlers.ofInputStream());
HttpResponse<InputStream> httpResponse = completableExchange.join();
InputStream responseBody = getBodyFromResponse(httpResponse);
callback.accept(responseBody);
}
Важным моментом является то, что метод Java HttpClient.sendAsync
возвращает CompletablFuture
, а затем для этого объекта вызывается join()
, чтобы дождаться ответа HTTP.принимается и возвращается как объект HttpResponse
, который затем используется для передачи тела ответа указанному методу обратного вызова.Но мой вопрос связан не с последовательностями запросов / ответов HTTP, а с рисками и рекомендациями при работе с потоками любого типа, которые поддаются структуре ожидания результатов и обратного вызова.