ResponseEntity를 사용하여 InputStream을 닫는 적절한 스트리밍 방법
어플리케이션 중 하나가 파일핸들이 누수되어 아직 원인을 찾지 못했습니다.
코드에는 다음과 같은 기능이 몇 가지 있습니다.
public ResponseEntity<InputStreamResource> getFoo( ... ) {
InputStream content = getContent(...)
InputStreamResource isr = new InputStreamResource(content);
return ResponseEntity.status(HttpServletResponse.SC_OK).body(isr);
}
(if
체크 및try
/catch
간략화를 위해 삭제)
이 섹션이 문제의 원인인 것은 확실합니다.이 특정 코드를 JMeter로 로드하면 다음과 같은 것을 알 수 있기 때문입니다.getContent()
이 단계에서는 실패합니다.
is = Files.newInputStream(f.toPath());
보통 나는 문을 닫는다.InputStream
이 짧고 간단한 코드 때문에 전에는 스트림을 닫을 수 없습니다.return
또는 의 호출body
.
내가 달릴 때lsof
(Linux에서 코드가 실행됨) 수천 개의 파일이 읽기 모드로 열려 있는 것을 알 수 있습니다.그래서 나는 이 문제가 스트림이 닫히지 않아서 생긴 것이라고 확신한다.
거래해야 할 모범 사례 코드가 있습니까?
StreamingResponseBody를 사용해 볼 수 있습니다.
StreamingResponseBody
응용 프로그램이 Servlet 컨테이너 스레드를 유지하지 않고 응답 OutputStream에 직접 쓸 수 있는 비동기 요청 처리를 위한 컨트롤러 메서드 반환 값 유형입니다.
다른 스레드에서 작업하고 있기 때문에 응답에 직접 쓰고 있는 문제, 호출해야 할 문제close()
전에return
해결되었습니다.
아마 당신은 다음 예시로 시작할 수 있을 것이다.
public ResponseEntity<StreamingResponseBody> export(...) throws FileNotFoundException {
//...
InputStream inputStream = new FileInputStream(new File("/path/to/example/file"));
StreamingResponseBody responseBody = outputStream -> {
int numberOfBytesToWrite;
byte[] data = new byte[1024];
while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
System.out.println("Writing some bytes..");
outputStream.write(data, 0, numberOfBytesToWrite);
}
inputStream.close();
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.bin")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(responseBody);
}
또, 다음과 같은 기능을 사용해 볼 수도 있습니다.Files
(Java 7 이후)
관리하지 않아도 됩니다.InputStream
File file = new File("/path/to/example/file");
StreamingResponseBody responseBody = outputStream -> {
Files.copy(file.toPath(), outputStream);
};
@Stackee007이 코멘트로 기술한 바와 같이, 프로덕션 환경에서 부하가 높은 경우, 다음을 정의하는 것이 좋습니다.@Configuration
을 위한 클래스TaskExecutor
매개 변수를 조정하고 관리합니다.Async
과정.
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
private final TaskExecutionProperties taskExecutionProperties;
public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) {
this.taskExecutionProperties = taskExecutionProperties;
}
// ---------------> Tune parameters here
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize());
executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize());
executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity());
executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix());
return executor;
}
// ---------------> Use this task executor also for async rest methods
@Bean
protected WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(getTaskExecutor());
}
};
}
@Bean
protected ConcurrentTaskExecutor getTaskExecutor() {
return new ConcurrentTaskExecutor(this.getAsyncExecutor());
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
mockMvc로 테스트하는 방법
통합 테스트에서 이 샘플 코드를 따를 수 있습니다.
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk()).getResponse().getContentAsByteArray();
콘텐츠 타입ResponseEntity<StreamingResponseBody>
는 입니다.MediaType.APPLICATION_OCTET_STREAM
이 예에서는 바이트[] (.getContentAsByteArray()
) 단, 본문 응답 콘텐츠 유형에 따라 String/Json/Plaintext를 모두 얻을 수 있습니다.
스프링을 사용하는 경우 메서드는 리소스를 반환하고 스프링이 나머지(기본 스트림 닫기 포함)를 처리하도록 할 수 있습니다.Spring API 내에서 이용할 수 있는 리소스 구현은 거의 없습니다.그렇지 않으면 독자적인 구현이 필요합니다.결국, 당신의 방법은 간단해지고 다음과 같은 것을 원합니다.
public ResponseEntity<Resource> getFo0(...) {
return new InputStreamResource(<Your input stream>);
}
로컬 파일을 읽고 내용을 HTTP 응답 본문으로 설정하는 모든 컨트롤러 메서드를 리팩터링할 수 있습니다.
「 」를하는 대신에, 「 」를 사용합니다.ResponseEntity
을 합니다.HttpServletResponse
를 복사해 주세요.getContent(...)
HttpServletResponse
Commons( Commons)의 IO 관련 IO 는 、 Google Guava 、 io io io 。어떤 경우에도 입력 스트림을 닫아야 합니다!다음 코드는 문 끝에 선언된 입력 스트림을 닫는 '리소스와 함께 시도' 문을 사용하여 이를 암묵적으로 수행합니다.
@RequestMapping(value="/foo", method=RequestMethod.GET)
public void getFoo(HttpServletResponse response) {
// use Java7+ try-with-resources
try (InputStream content = getContent(...)) {
// if needed set content type and attachment header
response.addHeader("Content-disposition", "attachment;filename=foo.txt");
response.setContentType("txt/plain");
// copy content stream to the HttpServletResponse's output stream
IOUtils.copy(myStream, response.getOutputStream());
response.flushBuffer();
}
}
참조:
https://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html https://google.github.io/guava/releases/19.0/api/docs/com/google/common/io/ByteStreams.html https://commons.apache.org/proper/commons-io/javadocs/api-release/index.html
방법(예: 방법, 방법, 방법)에 살펴보시기 바랍니다.public static int copy(InputStream input, OutputStream output) throws IOException
★★★★★★★★★★★★★★★★★」public static int copyLarge(InputStream input, OutputStream output) throws IOException
org.apache.commons.io.IOUtils
)
이것은InputStream
파일에서 코드는.이치노력하다
FileSystemResource fsr = new FileSystemResource(fileName);
return ResponseEntity.status(HttpServletResponse.SC_OK).body(fsr);
FileSystemResource
할 수 있다java.util.File
,a ,ajava.nio.file.Path
또는 a a a or도String
관련 파일을 가리킵니다.
언급URL : https://stackoverflow.com/questions/51845228/proper-way-of-streaming-using-responseentity-and-making-sure-the-inputstream-get
'source' 카테고리의 다른 글
reactjs에서 배경 이미지를 설정하는 방법 (0) | 2023.02.26 |
---|---|
포스트그레스 JSON 인덱스는 기존의 정규화 테이블에 비해 효율이 충분히 높은가요? (0) | 2023.02.26 |
리액트 라우터 v4 - GET할 수 없음 *url* (0) | 2023.02.26 |
Zuul 예외 사용자 지정 (0) | 2023.02.26 |
날짜별로 MongoDB ObjectId를 조회할 수 있습니까? (0) | 2023.02.26 |