우아한테크코스 레벨 4 강의 중, Tomcat의 accept-count, max-cennections, threads.max를 설정해보고 알아보는 미션이 있었습니다.
먼저 공식 문서에서는 위의 3개의 개념에 대해서 아래와 같이 설명하고 있습니다.
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 is8192
.Server가 요청을 받아들이고 처리하는 최대 Connection 수. Max-Connection에 도달하면, 서버는 요청을 받아들이지만 처리하지 않습니다. 서버가 새로운 요청을 받아들이고 처리할 수 있는 max-Connection 수 아래로 떨어질 때까지 추가적인 Connection은 block 됩니다. 한계(Max-Connection)에 도달하면 OS는 기본 값이 8192인 acceptCount에 따라 요청을 받아들입니다.
서버가 요청을 처리할 수 있는 Connection의 수를 의미합니다. Max-Connection이 10이라면, 10개의 요청이 처리중이라면, block되고 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 시킵니다.
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();
}
}
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만 유지할 수 있기 때문입니다.
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가 동시 작업을 잘 수행하는 것을 확인할 수 있습니다.
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초 가량의 많은 시간이 소요되는 것을 확인할 수 있습니다.
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개의 요청들이 동시에 처리된 것을 확인할 수 있습니다.
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의 개수가 적다면 요청을 처리하는데는 많은 시간을 소요하게 됩니다.
서버가 accept하고 process할 수 있는 요청의 개수를 뜻합니다.
요청의 개수가 Max-Connections를 초과했을 경우에, accept만 해둘 수 있는 요청의 개수
동시에 처리할 수 있는 스레드의 개수입니다. max-connections가 10이라도, 스레드의 개수가 2라면 동시에 처리할 수 있는 일은 2개입니다.
https://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Attributes
좋은 글이네요.