복붙노트

[SPRING] Springboot TomcatEmbeddedServletContainer KeepAliveTimeout이 작동하지 않습니다.

SPRING

Springboot TomcatEmbeddedServletContainer KeepAliveTimeout이 작동하지 않습니다.

나는 30 초로 봄 부트 임베디드 톰캣 서버에서 살아있는 타임 아웃을 설정했다. 그래서 아래 Application.java에서 사용합니다.

@Bean   
public EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
    TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
    containerFactory
            .addConnectorCustomizers(new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {
                    ((AbstractProtocol) connector.getProtocolHandler())
                            .setKeepAliveTimeout(30000);
                }
            });

    return containerFactory;
}

그런 다음 나머지 컨트롤러에서 40 초 동안 요청 스레드를 잠자 게됩니다. 하지만 나는 우편 배달부를 통해 요청을하면 HTTP 상태 코드 200을 성공적으로 반환하지만 대신 게이트웨이 시간 초과 오류가 반환됩니다.

setConnectionTimeout과 setKeepAliveTimeout을 모두 시도했지만 작동하지 않았습니다.

내가 여기서 무엇을 놓치고 있니?

질문 수정 : 내 초기 문제

내가 위의 질문을하게하는 나의 원래의 질문을 설명하겠습니다.

글쎄, 나는 일반적으로 5 개 이상의 minits에 대해 실행되는 긴 투표 프로세스가 있습니다.

그래서 longpoll을위한 Rest API를 호출 할 때 일어나는 일은 2.2 초 후에 브라우저에서 504 http 오류가 발생합니다.

AWS EC2 인스턴스에 ELB와 HAProxy가 설치된 AWS 환경을 사용하고 있습니다.

AWS doc에 따르면, ELB의 기본 Idle Connection Timeout은 60 초입니다. 그래서 나는 그것을 30 분까지 증가시켰다.

더욱 그것은 말한다,

위 코드 조각과 같이 내장 된 Tomcat keep-alive timeout을 30.2 분으로 늘려야합니다.

그래서 지금 나는 504 오류를 얻지 않고 내 긴 투표 요청이 완료 될 것을 기대합니다. 하지만 여전히 브라우저에서 504 오류가 발생합니까?

참조 : AWS dev 가이드

해결법

  1. ==============================

    1.모바일 장치에서 발생할 수있는 버려진 HTTP 연결을 닫으려는 것 같습니다.

    모바일 장치에서 발생할 수있는 버려진 HTTP 연결을 닫으려는 것 같습니다.

    @RestController
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        @Bean
        public EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
            TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
            containerFactory
                    .addConnectorCustomizers(new TomcatConnectorCustomizer() {
                        @Override
                        public void customize(Connector connector) {
                            ((AbstractProtocol) connector.getProtocolHandler()).setConnectionTimeout(100);
                        }
                    });
    
            return containerFactory;
        }
    
        @RequestMapping
        public String echo(@RequestBody String body) {
            return body;
        }
    }
    

    테스트를 빨리 실행하려면 연결 시간 초과가 100 밀리 초 코드로 설정되었습니다. 데이터는 청크로 보내집니다. 모든 청크 사이에서 실행중인 스레드는 x 밀리 초 동안 일시 중단됩니다.

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = DemoApplication.class)
    @WebIntegrationTest("server.port:19000")
    public class DemoApplicationTests {
    
        private static final int CHUNK_SIZE = 1;
        private static final String HOST = "http://localhost:19000/echo";
    
        @Rule
        public ExpectedException expectedException = ExpectedException.none();
    
        @Test
        public void slowConnection() throws Exception {
            final HttpURLConnection connection = openChunkedConnection();
            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
    
            writeAndWait(500, out, "chunk1");
            writeAndWait(1, out, "chunk2");
    
            out.close();
    
            expectedException.expect(IOException.class);
            expectedException.expectMessage("Server returned HTTP response code: 400 for URL: " + HOST);
    
            assertResponse("chunk1chunk2=", connection);
        }
    
        @Test
        public void fastConnection() throws Exception {
            final HttpURLConnection connection = openChunkedConnection();
            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
    
            writeAndWait(1, out, "chunk1");
            writeAndWait(1, out, "chunk2");
    
            out.close();
    
            assertResponse("chunk1chunk2=", connection);
        }
    
        private void assertResponse(String expected, HttpURLConnection connection) throws IOException {
            Scanner scanner = new Scanner(connection.getInputStream()).useDelimiter("\\A");
            Assert.assertEquals(expected, scanner.next());
        }
    
        private void writeAndWait(int millis, OutputStreamWriter out, String body) throws IOException, InterruptedException {
            out.write(body);
            Thread.sleep(millis);
        }
    
        private HttpURLConnection openChunkedConnection() throws IOException {
            final URL url = new URL(HOST);
            final HttpURLConnection  connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setChunkedStreamingMode(CHUNK_SIZE);
            return connection;
        }
    }
    

    org.apache.catalina.core 패키지의 로그 수준을 DEBUG로 설정하십시오.

    logging.level.org.apache.catalina.core=DEBUG
    

    slowConnection 테스트에서 SocketTimeoutException을 볼 수 있습니다.

    HTTP 상태 코드 502를 오류 응답 상태로 왜 원하는지 모르겠습니다. HTTP 502 메시지 :

    클라이언트 Postman이 서버 응용 프로그램을 호출합니다. 그 사이에 어떤 게이트웨이 나 프록시도 보이지 않습니다.

    질문을 최소로 압축했다가 실제로 프록시를 만들고 싶다면 Netflix Zuul을 사용해보십시오.

    23.03.2016 업데이트 :

    이것이 Stackoverflow에 대한 OP의 질문에 대한 근본 원인입니다.

    이 구현은 실제로 Tomcat 작업자 스레드가 새 HTTP 요청을 처리하는 것을 방지합니다. 결과적으로 추가 실행 시간이 길 때마다 요청 처리량이 감소합니다.

    장기 실행 작업을 별도의 스레드로 오프로드하는 방법을 제안합니다. 클라이언트 (브라우저)는 결과를 가져 오기위한 새 요청을 시작합니다. 처리 상태에 따라 서버는 결과 또는 통지 / 오류 / 경고 /를 리턴합니다.

    다음은 매우 간단한 예입니다.

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import static org.springframework.http.HttpStatus.CREATED;
    import static org.springframework.http.HttpStatus.NOT_FOUND;
    import static org.springframework.http.HttpStatus.OK;
    
    @RestController
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
        private ExecutorService executorService = Executors.newFixedThreadPool(10);
        private Map<String, String> results = new ConcurrentHashMap<>();
    
        @RequestMapping(path = "put/{key}", method = RequestMethod.POST)
        public ResponseEntity<Void> put(@PathVariable String key) {
            executorService.submit(() -> {
                try {
                    //simulate a long running process
                    Thread.sleep(10000);
                    results.put(key, "success");
                } catch (InterruptedException e) {
                    results.put(key, "error " + e.getMessage());
                    Thread.currentThread().interrupt();
                }
            });
            return new ResponseEntity<>(CREATED);
        }
    
        @RequestMapping(path = "get/{key}", method = RequestMethod.GET)
        public ResponseEntity<String> get(@PathVariable String key) {
            final String result = results.get(key);
            return new ResponseEntity<>(result, result == null ? NOT_FOUND : OK);
        }
    }
    
  2. from https://stackoverflow.com/questions/36076737/springboot-tomcatembeddedservletcontainer-keepalivetimeout-not-working by cc-by-sa and MIT license