Tomcat의 max-connections, accept-count, threads.max

공병주(Chris)·2022년 9월 13일
3
post-thumbnail

우아한테크코스 레벨 4 강의 중, Tomcat의 accept-count, max-cennections, threads.max를 설정해보고 알아보는 미션이 있었습니다.

먼저 공식 문서에서는 위의 3개의 개념에 대해서 아래와 같이 설명하고 있습니다.

MaxConnections

The maximum number of connections that the server will accept and process at any given time. When this number has been reached, the server will accept, but not process, one further connection. This additional connection be blocked until the number of connections being processed falls below maxConnections at which point the server will start accepting and processing new connections again. Note that once the limit has been reached, the operating system may still accept connections based on the acceptCount setting. The default value is 8192.

Server가 요청을 받아들이고 처리하는 최대 Connection 수. Max-Connection에 도달하면, 서버는 요청을 받아들이지만 처리하지 않습니다. 서버가 새로운 요청을 받아들이고 처리할 수 있는 max-Connection 수 아래로 떨어질 때까지 추가적인 Connection은 block 됩니다. 한계(Max-Connection)에 도달하면 OS는 기본 값이 8192인 acceptCount에 따라 요청을 받아들입니다.

서버가 요청을 처리할 수 있는 Connection의 수를 의미합니다. Max-Connection이 10이라면, 10개의 요청이 처리중이라면, block되고 acceptCount 설정에 따라 제어됩니다.

AcceptCount

The maximum length of the operating system provided queue for incoming connection requests when maxConnections has been reached. The operating system may ignore this setting and use a different size for the queue. When this queue is full, the operating system may actively refuse additional connections or those connections may time out. The default value is 100.

maxConnection에 도달했을 때, Connection 요청에 대해 OS에서 제공하는 queue의 최대 길이입니다. OS는 이 설정을 무시하고 다른 size의 queue를 사용할 수 있습니다. queue가 full이라면, OS는 추가적인 요청을 거절하거나 timeOut 시킵니다. 기본 값은 100입니다.

Connection의 수가 Max-Connection에 도달했을 때, 추가적인 Connection을 대기시키는 공간입니다.

Max-Connection에 도달한 상태이고 AcceptCount가 10이라면, 추가적인 요청 10개를 받아들일 수 있습니다. 받아들이기만 하지, 처리하지는 않습니다. 그리고 11번째 추가 요청에은 거절하거나 timeout 시킵니다.

Max-Thread

The maximum number of request processing threads to be created by this Connector
, which therefore determines the maximum number of simultaneous requests that can be handled. If not specified, this attribute is set to 200.

요청을 처리하는 Thread의 최대 수입니다. 처리될 수 있는 동시 요청의 최대 수를 결정하는 것입니다. 기본 값은 200입니다.

말 그대로 동시 요청을 처리할 수 있는 Thread의 개수입니다.

이게 어떻게 쓰이는걸까?

이 3가지 값을 바꾸면서, 요청이 어떻게 받아들여지고 처리되는지 확인해보겠습니다.

먼저 테스트 코드는 아래와 같습니다.

@Test
void test() throws Exception {
    final var NUMBER_OF_THREAD = 10;
    var threads = new Thread[NUMBER_OF_THREAD];

    for (int i = 0; i < NUMBER_OF_THREAD; i++) {
        threads[i] = new Thread(() -> incrementIfOk(TestHttpUtils.send("/test")));
    } // TestHttpUtils.send("/test")에서 요청을 보냄

    for (final var thread : threads) {
        thread.start();
        Thread.sleep(50);
    }

    for (final var thread : threads) {
        thread.join();
    }
}

private static void incrementIfOk(final HttpResponse<String> response) {
    if (response.statusCode() == 200) {
        count.incrementAndGet();
    }
}

10개의 요청을 아래의 Controller로 보내도록 했습니다. 그리고 요청을 처리하는 시간을 확인해보겠습니다.

@Controller
public class SampleController {

    private static final Logger log = LoggerFactory.getLogger(SampleController.class);

    private static final AtomicInteger count = new AtomicInteger(0);

    private final HelloWorldService helloWorldService;

    @Autowired
    public SampleController(final HelloWorldService helloWorldService) {
        this.helloWorldService = helloWorldService;
    }

    @GetMapping("/test")
    @ResponseBody
    public String helloWorld() throws InterruptedException {
        Thread.sleep(500);
        log.info("http call count : {}", count.incrementAndGet());
        return helloWorldService.helloWorld();
    }
}

1. max-connections가 1, accept-count가 1일 때

max-connections를 1로 설정하고 accept-count를 1로 설정하여 자원을 많이 할당하지 않아보았습니다.

server:
  tomcat:
    accept-count: 1
    max-connections: 1
    threads:
      max: 2
2022-09-13 21:40:37.161  INFO 8384 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 1
2022-09-13 21:40:38.852  INFO 8384 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 2

위와 같이 2개의 요청만을 처리할 수 있었습니다. max-connections가 1이라서 10개의 요청 중, connection은 1개만 맺을 수 있습니다. 그리고 해당 connection에 대한 처리가 일어나는 동안, 남은 9개 중 1개의 요청은 accept-count에 해당하는 size가 1인 queue에 대기하게됩니다. 첫번째의 요청이 처리되면 queue에 대기중인 요청이 처리가 됩니다. 그리고 나머지 8개의 요청은 아래와 같이 time out 처리가 됩니다.

Exception in thread "Thread-6" java.lang.RuntimeException: java.net.http.HttpConnectTimeoutException: HTTP connect timed out
	at concurrency.stage2.TestHttpUtils.send(TestHttpUtils.java:26)
	at concurrency.stage2.AppTest.lambda$test$0(AppTest.java:30)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.net.http.HttpConnectTimeoutException: HTTP connect timed out
	at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:555)
	at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:119)
	at concurrency.stage2.TestHttpUtils.send(TestHttpUtils.java:24)
	... 2 more 

threads.max의 값을 바꾸어도 결과는 동일합니다. max-connection이 1이기 때문에 하나의 요청에 대해 응답하고나서야 다음 요청에 대한 처리가 가능합니다. 따라서, 위의 테스트 환경에서 thread의 개수는 1이던 100이던 결과에 영향을 미치지 못합니다. 1개의 Connection만 유지할 수 있기 때문입니다.

2. Max-Connections가 충분할 때

2-1. Thrads.max가 10일 때

server:
  tomcat:
    accept-count: 1
    max-connections: 10
    threads:
      max: 10
2022-09-13 21:57:07.890  INFO 8631 --- [nio-8080-exec-3] concurrency.stage2.SampleController      : http call count : 4
2022-09-13 21:57:07.890  INFO 8631 --- [nio-8080-exec-2] concurrency.stage2.SampleController      : http call count : 5
2022-09-13 21:57:07.890  INFO 8631 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 1
2022-09-13 21:57:07.890  INFO 8631 --- [nio-8080-exec-4] concurrency.stage2.SampleController      : http call count : 3
2022-09-13 21:57:07.890  INFO 8631 --- [nio-8080-exec-5] concurrency.stage2.SampleController      : http call count : 2
2022-09-13 21:57:07.898  INFO 8631 --- [nio-8080-exec-6] concurrency.stage2.SampleController      : http call count : 6
2022-09-13 21:57:07.930  INFO 8631 --- [nio-8080-exec-8] concurrency.stage2.SampleController      : http call count : 7
2022-09-13 21:57:07.933  INFO 8631 --- [nio-8080-exec-7] concurrency.stage2.SampleController      : http call count : 8
2022-09-13 21:57:07.993  INFO 8631 --- [nio-8080-exec-9] concurrency.stage2.SampleController      : http call count : 9
2022-09-13 21:57:08.045  INFO 8631 --- [io-8080-exec-10] concurrency.stage2.SampleController      : http call count : 10

실행 결과를 확인해보면 큰 시간 차이 없이 10개의 Thread가 동시 작업을 잘 수행하는 것을 확인할 수 있습니다.

2-2. Threads.max가 1일 때

server:
  tomcat:
    accept-count: 1
    max-connections: 10
    threads:
      max: 1
2022-09-13 21:55:49.647  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 1
2022-09-13 21:55:50.174  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 2
2022-09-13 21:55:50.680  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 3
2022-09-13 21:55:51.183  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 4
2022-09-13 21:55:51.688  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 5
2022-09-13 21:55:52.196  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 6
2022-09-13 21:55:52.703  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 7
2022-09-13 21:55:53.207  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 8
2022-09-13 21:55:53.713  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 9
2022-09-13 21:55:54.214  INFO 8612 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 10

max-Connections이 10이라서 모든 요청에 대해 connection은 유지합니다. 하지만, Thread가 1개인 경우에는 동시에 처리할 수 있는 요청의 수가 1개입니다. 따라서, 10개의 요청을 처리하는데 5초 가량의 많은 시간이 소요되는 것을 확인할 수 있습니다.

3. max-connections가 5, accept-count가 5일 때

3-1. threads.max가 5일 때,

10개의 요청 중 5개는 바로 Connection을 맺도록 하고, 5개는 accept-count에 대기하도록 하였습니다. 또, thread.max를 5로 설정하여 5개의 요청을 동시처리 가능하도록 했습니다.

server:
  tomcat:
    accept-count: 5
    max-connections: 5
    threads:
      max: 5
2022-09-13 22:02:59.871  INFO 8725 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 2
2022-09-13 22:02:59.871  INFO 8725 --- [nio-8080-exec-2] concurrency.stage2.SampleController      : http call count : 1
2022-09-13 22:02:59.871  INFO 8725 --- [nio-8080-exec-4] concurrency.stage2.SampleController      : http call count : 4
2022-09-13 22:02:59.871  INFO 8725 --- [nio-8080-exec-5] concurrency.stage2.SampleController      : http call count : 5
2022-09-13 22:02:59.871  INFO 8725 --- [nio-8080-exec-3] concurrency.stage2.SampleController      : http call count : 3
2022-09-13 22:04:00.464  INFO 8725 --- [nio-8080-exec-3] concurrency.stage2.SampleController      : http call count : 9
2022-09-13 22:04:00.464  INFO 8725 --- [nio-8080-exec-2] concurrency.stage2.SampleController      : http call count : 6
2022-09-13 22:04:00.464  INFO 8725 --- [nio-8080-exec-5] concurrency.stage2.SampleController      : http call count : 7
2022-09-13 22:04:00.464  INFO 8725 --- [nio-8080-exec-4] concurrency.stage2.SampleController      : http call count : 8
2022-09-13 22:04:00.464  INFO 8725 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 10

최초로 Connection을 맺은 5개의 요청이 동시에 처리되고, 처리된 후에 queue에 있던 5개의 요청들이 동시에 처리된 것을 확인할 수 있습니다.

3-1. threads.max가 1일 때,

server:
  tomcat:
    accept-count: 5
    max-connections: 5
    threads:
      max: 1
2022-09-13 22:17:09.587  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 1
2022-09-13 22:17:10.118  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 2
2022-09-13 22:17:10.623  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 3
2022-09-13 22:17:11.130  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 4
2022-09-13 22:17:11.637  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 5
2022-09-13 22:18:10.194  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 6
2022-09-13 22:18:11.704  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 7
2022-09-13 22:18:12.211  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 8
2022-09-13 22:18:12.719  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 9
2022-09-13 22:18:13.228  INFO 8895 --- [nio-8080-exec-1] concurrency.stage2.SampleController      : http call count : 10

max-connections이 5이고, accept-count가 5이라서 timeout 처리되는 요청은 없지만

처리할 수 있는 Thread의 개수가 1개이기 때문에 10개의 요청을 처리하는데, 많은 시간이 소요됩니다.

max-connections의 개수가 1000이라도 Thread의 개수가 적다면 요청을 처리하는데는 많은 시간을 소요하게 됩니다.

정리

max-connections

서버가 accept하고 process할 수 있는 요청의 개수를 뜻합니다.

accept-count

요청의 개수가 Max-Connections를 초과했을 경우에, accept만 해둘 수 있는 요청의 개수

threads.max

동시에 처리할 수 있는 스레드의 개수입니다. max-connections가 10이라도, 스레드의 개수가 2라면 동시에 처리할 수 있는 일은 2개입니다.

참고 자료

https://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Attributes

profile
self-motivation

4개의 댓글

comment-user-thumbnail
2022년 9월 18일

좋은 글이네요.

답글 달기
comment-user-thumbnail
2023년 3월 19일

잘 읽고가요.
3개 설정 값에 대해 테스트 케이스를 잘 나누신 것 같아요.

답글 달기
comment-user-thumbnail
2023년 9월 14일

감사합니다 많은 도움이 됬어요.
케이스를 꼼꼼하게 나눠주셔서 이해하기 수월했습니다.

답글 달기
comment-user-thumbnail
2024년 4월 5일

좋은글 감사합니다! 다름아니라 2-2번 케이스 (Threads.max가 1일 때)는 처음 스레드에서 작업처리하는 1개를 제외한 나머지 커넥션 9개가 유지되는데, 해당 커넥션들은 어디서 대기하고 있는 것일까요?? PollerQueue에서 대기하는 걸까요?

답글 달기