Я уже некоторое время использую Java Spring framework для своей работы. Я начал изучать платформу Spring во время создания приложения REST API, которое должно было подключаться к другим удаленным службам через их собственный интерфейс REST API. В то время как Spring RestTemplate в настоящее время находится только в режиме обслуживания и, как ожидается, в будущем полностью устареет, это было первое, что я нашел для выполнения HTTP-запросов в Spring framework. Так что я просто начал использовать его из соображений практичности.
Теперь мы часто хотим регистрировать HTTP-запросы, которые делают наши серверы. Один из способов – поместить операторы журнала везде, где нам нужно было совершать HTTP-вызовы. Одной из альтернатив с помощью Spring RestTemplate является настройка перехватчика ведения журнала . Таким образом, мы можем настроить глобальные (или несколько) шаблонов Rest, в которых настроена возможность ведения журнала.
public class LoggingInterceptor implements ClientHttpRequestInterceptor { static Logger log = LoggerFactory.getLogger(LoggingInterceptor.class); @Override public ClientHttpResponse intercept(HttpRequest req, byte[] reqBody, ClientHttpRequestExecution ex) throws IOException { log.debug("Request body: {}", new String(reqBody, StandardCharsets.UTF_8)); ClientHttpResponse response = ex.execute(req, reqBody); InputStreamReader isr = new InputStreamReader(response.getBody(), StandardCharsets.UTF_8); String body = new BufferedReader(isr) .lines() .collect(Collectors.joining("\n")); log.debug("Response body: {}", body); return response; } }
Взяв из сообщения Baeldung выше, мы можем видеть, что подпись для метода перехват
содержит тело запроса
параметр типа байт []
. Это означает, что для регистрации тела исходящего запроса Spring должен сериализовать все, что мы отправляем, в структуру массива байтов в памяти. В зависимости от отправляемого вами тела запроса это может вызвать проблемы с памятью и, возможно, даже ошибки нехватки памяти. Кроме того, это, вероятно, влияет на производительность вашего приложения, даже если немного. Так что это то, что может потребовать некоторых соображений.
Теперь в моем текущем проекте я должен ретранслировать загруженные файлы, созданные клиентами, в другую службу через ее вызов API. Эти файлы потенциально могут иметь размер в гигабайтах. Все еще используя RestTemplate, я сразу понимаю, что у приложения закончится память, если я войду в систему с помощью описанного выше перехватчика. Поэтому я решил создать два вида RestTemplate: один, который может автоматически регистрировать запросы, и другой, который этого не делает. Кроме того, я использовал HttpComponentsClientHttpRequestFactory.setbufferrequestbody(false) , чтобы гарантировать, что приложение не будет буферизировать загруженный файл, который необходимо ретранслировать.
Но когда я функционально тестировал приложение, я продолжал работать с ошибками нехватки памяти при загрузке файла объемом 1,5 ГБ, и что бы я ни делал, я не мог это исправить. После дальнейшего расследования выясняется, что я не знал, что Привод пружинной загрузки доступен в пути к классам. Одна из вещей, которую предоставляет пакет Actuator, – это набор метрик исходящих HTTP-клиентов, созданных resttemplates, который автоматически предоставляется, если мы создаем нашу RestTemplate
с помощью автоматически настроенного RestTemplateBuilder
, предоставляемого SpringBoot. То есть, если бы мы создали нашу RestTemplate следующим образом:
@Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); }
затем автоматически настраивается возможность сбора показателей. И, конечно же, сбор метрик выполняется путем добавления перехватчика, если быть точным, используется Настройщик шаблона Rest метрик . Этот перехватчик добавляется в Конструктор шаблонов Rest
Я использовал выше для создания RestTemplate
, и поэтому все мои запросы, сделанные с помощью RestTemplate
то, что не было настроено для ведения журнала, на самом деле буферизовало тело запроса! Неудивительно, что я продолжал попадать в ошибки ООМ…
Ну, исправление на самом деле довольно простое, я только что создал RestTemplate
без настроенного Rest TemplateBuilder
, и все в порядке! Ну… в конце концов я решил перенести приложение на использование WebClient вместо этого. Мало того, что это предлагаемый API для использования сейчас, таким образом, я создаю приложение для будущего доказательства, я все равно смогу регистрировать запросы без необходимости принудительного буферизации тела запроса. Хотя, если я захочу зарегистрировать тело запроса программно, мне все равно потребуется буферизировать тело запроса, но это отдельная проблема на будущее.
Оригинал: “https://dev.to/btruhand/request-body-buffering-with-spring-s-resttemplate-1958”