Nginx + Spring Boot (Tomcat)에서 업로드 크기 초과시 오류 응답이 지연됨

지금 종사하고 있는 이슈로, 파일 업로드시에 사이즈 초과 에러가 발생하면 에러 리스폰스가 지연되는 사건에 부딪혀 버렸습니다. 실은 꽤 전부터 부딪치고 있었습니다만, 드디어 최종적인 대응 방법을 결정하지 않으면 안 되어 왔기 때문에, 조금 진지하게 조사해 보았습니다.

사건



이벤트로는 요청 후 약 60 초 후에 오류 응답이 반환되었습니다. 로그를 보는 한은 앱적으로는 더 빨리 검지하고 있습니다만・・・.
이 이벤트는 분명히 응답 (Transfer-Encoding: chunked)시 발생하는 것으로 보이며 JSON 문자열 (Content-Length이있는 상태)에서 반환하면 지연이 발생하지 않았습니다. 이 이벤트가 발생한 앱에서는 오류 응답을 Spring MVC+Jackson 메커니즘( HttpMessageConverter )을 사용하여 JavaBean->JSON으로 만들었으므로 분할 응답을 처리합니다.
어디에서 처리가 멈추고 있는지 디버그 실행해 조사해 보았는데···사이즈 초과 검지 후에 나머지의 리퀘스트 데이터를 읽어들이는 처리의 곳에서 처리가 멈추고 있었습니다. 분명히 · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·

Spring (Spring Boot) 앱



업로드용 컨트롤러는 이런 느낌.
@RestController
public class FileUploadRestController {

    @RequestMapping("/files")
    String upload(MultipartFile multipartFile) {
        return multipartFile.getOriginalFilename();
    }

    @ExceptionHandler
    @ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
    public ApiError handleMultipartException(MultipartException e) {
        ApiError error = new ApiError();
        error.setCode("UPLOAD_SIZE_ERROR");
        return error;
    }

}

에러시의 JSON을 표현하는 JavaBean은 이런 느낌.
public class ApiError implements Serializable {
    private String code;

    public String getCode() {
        return code;
    }
}

Spring MVC 예외 처리기에서 오류를 감지할 수 있도록 멀티파트 요청의 구문 분석을 지연시킵니다.

@Configuration
public class WebMvcConfig {
    @Bean
    StandardServletMultipartResolver multipartResolver() {
        StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
        resolver.setResolveLazily(true); // trueを指定すると解析を遅延させることができる
        return resolver;
    }
}

덧붙여 상기 앱 코드(본 이벤트의 검증용으로 사용한 앱)는 GitHub에 공개되어 있습니다.
  • htps : // 기주 b. 코 m / 카즈키 43 조오 / 닌긴 x-sp 링 g 보오 t-

  • 원인은?



    본질적인 원인인가는 꽤 수상합니다만···
    Nginx가 백 서버에 프록시 할 때 HTTP 버전이 1.0 (Nginx의 기본값으로 남아 있음)이이 이벤트를 일으켰습니다. 분할 응답은 HTTP 1.1에서 지원되는 구조이므로 Tomcat 측에서 수행하는 처리가 HTTP 1.0용으로 된 것으로 예상대로의 움직임이 되지 않았던 것 같습니다. (어쩐지 Tomcat 측에도 뭔가 문제가있는 것 같은 생각은 들지만 ...)

    Nginx 프록시시 HTTP 버전을 1.1로 설정



    프록시시 HTTP 버전 지정은 proxy_http_version을 사용합니다.
    http {
        # ...
        upstream spring-boot {
            server springboot:8080;
        }
        # ...
        server {
            listen       80;
            server_name  localhost;
            # ...
            client_max_body_size  100m;
            # ...
            location / {
                proxy_pass http://spring-boot/;
                proxy_http_version 1.1; #### これを追加 ###
            }
            # ...
        }
        # ...
    }
    

    요약



    어쩌면 부딪치고 있던 사건은 해결할 것 같지만 ···· 어쩐지 모야 모야감을 취할 수 없구나~
    지금의 프로젝트···Nginx에 자세한 사람이 없지만, 왠지 사용하고 있는 w

    좋은 웹페이지 즐겨찾기