일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 자바 ORM 표준 JPA 프로그래밍
- Stack
- Kotlin
- 이스티오
- OS
- JPA
- Real MySQL
- 토비의 스프링
- Stream
- MSA
- 토비의 스프링 정리
- redis
- SpringBoot
- spring
- 쿠버네티스
- 보조스트림
- 백준
- K8s
- 스트림
- jvm
- IntellJ
- 스프링
- mysql
- 자바
- Java
- GC
- Collection
- gradle
- thread
- list
- Today
- Total
인생을 코딩하다.
[자바 성능 튜닝] 서버를 어떻게 세팅해야 할까? 본문
저는 현재 프로젝트를 진행함에 있어,
https://github.com/f-lab-edu/black-postoffice
지속적으로 성능을 튜닝하여 빠른 서비스를 만들어야 한다고 생각합니다.
서비스의 속도가 계속 느려진다면, 이용하는 사용자들의 수도 그만큼 줄어들겠죠? 이건 곧 수익의 저하로도 이어집니다. 최악의 시나리오네요.
따라서 저는 제가 진행중인 프로젝트의 성능을 꾸준히 튜닝하여 속도를 높이기 위해
http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788966260928
위의 책을 읽어보고 있습니다.
"14장 - 서버를 어떻게 세팅해야할까?"의 챕터에 너무 좋은 내용들이 많아서 깊게 학습한 후에 제 프로젝트에 적용해본다면, 훨씬 좋은 성능을 기대할 수 있을 것이라고 생각이 드는데요.
그래서 "서버를 어떻게 세팅해야할까?"라는 글을 작성하게 되었습니다.
아파치 웹 서버의 설정
웹 서버는 반드시 WAS 앞에 두어야 합니다.
정적인 부분은 웹 서버에서 처리해야 합니다. 그렇지 않으면 WAS 서버에서 웹 서버의 역할까지 수행 합니다.
웹 서버를 WAS 서버 앞에 두지 않으면 이미지, CSS, 자바 스크립트, HTML 등을 처리하느라 아까운 WAS 서버의 스레드를 점유하게 됩니다. 반드시 사용 웹 서버나 아파치 웹 서벌-버를 WAS 앞 단에 두고 운영하는 것이 좋습니다.
위의 내용들에 관해서는, 제가 정리한 Web server와 WAS의 차이에 관해 읽어보시면 더욱 깊게 이해하실 수 있으 실겁니다.
계속 이야기 해보도록 하겠습니다.
아파치의 설정을 바꾸기 위한 httpd.conf
아파치 웹 서버는 MPM이라는 것을 사용합니다. 여러 개의 프로세싱 모듈 기반의 서비스를 제공한다는 의미입니다.
제가 정리한 이 글을 읽어보시면 MPM에 관해 자세히 알아보실 수 있습니다.
아파치의 설정을 바꾸기 위해서는 httpd.conf 파일을 수정하셔야 합니다. 이 파일의 중간을 보시면
ThreadsPerchild 250
MaxRequestPerchild 0
위와 같은 설정을 보실 수 있습니다.
ThreadsPerChild는 웹 서버가 사용하는 스레드의 개수를 지정합니다. 위와 같이 지정하면, 아파치 프로세스 하나당 250개의 수레드가 만들어집니다. 만약 이 수치가 적게 지정되어 있다면, 이 수치를 늘려주어야 합니다. 그래야 서버가 더 많은 사용자의 요청을 처리할 수 있게 됩니다.
MaxRequestsPerChild는 최대 요청 개수를 지정하는 부분입니다. 0이면 그 수에 제한을 두지 않겠다는 의미가 됩니다. 만약 이 값을 10으로 둔다면, 그 이상의 처리는 하지 않게됩니다. 따라서 가급적 기본값인 0으로 두고 사용하는 것을 권장드립니다.
스레드와 관련된 내용을 보다 세밀하게 지정하려면 httpd-mpm.conf 파일을 수정
세밀한 스레드 설정 정보를 지정하기 위해서는 위 파일의 worker 부분을 수정해주어야 합니다.
worke 부분에는 지정된 여러 설정 값들이 있습니다. 여기서 우리가 중요하게 알아야 할 것은 프로세스, 쓰레드의 갯수와 이것들이 처리할 수 있는 요청의 수 입니다.
기본적으로 프로레스 수가 2개이고, 프로세스당 스레드 수가 25이므로 기본적으로 50개의 요청을 처리할 수 있습니다.
또한 최대 여유 스레드가 75개이므로, 최대한 사용 가능한 클라이언트 수는 150이 됩니다. 그리고 150이 최대 MaxClient의 수 입니다.
이렇게 설정되어 있을 때 사용자가 늘어나거나 WAS가 멈추면 어떤 상황이 발생할까요?
초당 150명의 요청을 받고 있다고 가정해봅시다. 그 요청을 전달받은 WAS도 마찬가지로 150명의 요청을 받습니다.
그런데, 자바는 GC를 할 때 JVM 자체가 멈춥니다. 만약 이 GC가 2초가 걸리면 어떻게 될까요?
아파치 웹 서버에 총 300명의 요청이 기다리게 될 것이니다. 그런데 GC를 하는 동안 WAS가 멈추기 때문에 새로운 연결을 할 수가 없습ㅂ니다. 이 경우 Tomcat에서는 API Connector라는 웹 서버와 WAS 사이의 커넥터에 설정한 backlog라는 값의 영향을 받습니다.
만약 이 값을 서정하지 않으면 기본값은 100입니다.
즉, WAS가 응답하지 않을 떄 100개의 요청까지 큐에 담아둔다는 말입니다. 따라서 이 100개를 넣는 요청들은 503 이라는 HTTP 헤더 콛 값을 리턴 받게 됩니다. 그러면 사용자에게 50이라는 에러 메시지가 화면에 뿌려질 것 입니더. 이러한 값을 받지 않으려면 다음과 같은 네 가지의 조치를 고민해보는 것이 좋습니다.
- 서버를 늘린다.
- 서비스를 튜닝한다
- GC 튜닝을 한다
- 각종 옵션값을 튜닝한다.
웹 서버의 Keep Alive
모든 웹 서버를 설정할 때 또 한 가지 중요한 값이 있습니다. 바로 KeppAlive 설정입니다. 아파치 웹 서버의 경우 gttpd.conf 파일에 다음의 설정이 없으면 간단하게 한 줄 추가하면 됩니다.
keepAlive On
웹 서버와 웹 브라우저가 연결이 되었을 때 keepAlivce 기능이 켜져 있지 않으면, 매번 HTT 연결을 맺었다 끊었다 하는 작업을 반복합니다.
하지만, KeepAlive 기능이 켜져 있으면 두 개 정도의 연결을 열어서 끊지 않고, 연결을 계속 재사용합니다. 이렇게 되면 연결을 하기 위한 대기시간이 짧아지기 때문에 사용자가 느끼는 응답 속도도 엄청나게 빨라집니다.
DB Connection Pool 및 스레드 개수 설정
대부분의 WAS에서는 DB 커넥션 풀의 개수를 최소치, 증가치, 최대치 등으로 자세하게 지정할 수 있습니다.
최소치는 말 그대로 서버가 가동될 때 연결을 수행하는 갯수입니다. 로컬 환경에서는 빠르게 개발하기 위해서 가장 최소 값으로 지정하는 것이 좋습니다.
하지만 운영 중에는 최소 및 최대 값을 동일하게 하는 것이 좋습니다. 사용자 수가 갑자기 증가하면 DB 커넥션 풀의 개수도 증가되어야 하고 증가할 때 대기 시간이 발생할 확률이 크기 때문입니다. 만약 DB 서버의 리소스가 부족하다면 최소 값을 적게 해놓는 것도 한 방법이 될 수 있습니다.
대부분 WAS에서 두 설정 값의 기본 개수가 10~20개 정도입니다. 따라서 기본 값으로 서비스를 오픈하면 서버가 원하는 요청량을 처맇지 못하게 됩니다. DB 커넥션 풀은 보통 40~50개로 지정하며, 스레드 개수는 이보다 10개 정도 더 지정합니다. 이렇게 지정한 이유는 스레드 개숙 DB 커넥션 풀의 개수보다 적으면 적은 수만큼의 연결은 필요 없기 때문입니다. 예를 들어 400개로 지정하고, DB 커넥션 풀 개수를 50개로 지정하면, 적어도 10의 연결을 사용하지 않게 됩니다.
커넥션 풀에 관해서는 커넥션 풀을 사용하지 않았을때 발생하는 "비용"은 구체적으로 어떤 비용일까? 이 글도 한 번 읽어보시면 좋을 것 같습니다.
그럼 스레드의 개수가 DB 연결 개수보다 많아야 하는 이유는 무엇일까요?
모든 애플리케이션이나 화면이 DB에 접속하는 것은 아닙니다. 또한 관리자 콘솔을 사용하여 서버에 접속할 수도 있기 때문에, 그만큼 여유분을 갖도록 지정하는 것이 보통입니다.
그러면 가장 적합한 DB 커넥션 풀과 스레드 개수는 몇 개일까요?
웹 서버의 세팅 값도 그러지만, 이 수치도 서버와 애플리케이션 상황에 따라 다르며, 완벽한 값은 없습니다. 서비스 및 서버의 환경에 맞게 값을 지정해야 하는 거십니다. 가장 좋은 방법은 성능 테스트를 통해서 가장 적절한 값을 구하는 것입니다.
DB 커넥션 풀의 개수를 기준으로 적절한 수치를 찾는 방법을 생각해봅시다. DB 커넥션 풀을 40개로 잡아 놓았다고 가정해봅시다. 40개 전부를 사용하면서 DB의 CPU 사용량이 100%에 도달했다면, 어떻게 해야 할까요? 이 경우에는 DB의 CPU를 점유하는 쿼리를 찾아서 튜닝을 수행해야 합니다. 다시 말하면, 인덱스가 없더나 테이블을 풀 스캔하는 쿼리가 있는 것은 아닌지 쿼리의 플랜을 떠서 확인해 보아야 합니다. 40개를 전부 사용한다고 해서 DB 커넥션 풀 개수를 80~200개로 늘리면, 모든 DB와의 연결을 전부 사용하고 응답 시간은 엄청나게 느려질 것입니다.
이번에는 DB의 CPU 사용령은 50%도 되자 않는 상황에서 WAS의 CPU 사용량이 100%의 도달하고 있는 상황을 생각해봅시다. 그때 사용하는 DB 커넥션 풀의 갸수는 20개 정도밖에 되지 않습니다.
이럴때는 어떻게 할까요?
이 경우에는 WAS의 애플리케이션을 튜닝해야 합니다. 하지만 이미 튜닝이 된 상태라면 이 서버의 DB 커넥션 풀의 개수는 약간의 여유를 두기 위해서 25~30개 정도로 지정하는 것이 좋습니다.
여기서 잠시 주의할 점은 이러한 경우에 튜닝할 생각은 하지 않고, 서버의 대수를 늘리는 경우가 많습니다. 서버를 늘리는 것은 가장 마지막에 해야한다는 사실을 잊으면 안되고, 서버를 늘리는 이유에 관해 명확한 이유가 있어야 합니다.
그 이유는 이 글을 참고해보시면 좋을 것 같네요.
작성 중....
'Java' 카테고리의 다른 글
[Effective Java -Item2] 생성자에 매개변수가 많으면 빌더를 고려하라 (0) | 2021.08.28 |
---|---|
[Effective Java -Item2] 생성자 대신 정적 팩토리 메소드를 고려하라 (0) | 2021.08.28 |
[JAVA] 싱글톤 패턴은 객체가 단 1개만 생성되는 것을 보장할까? 및 자바에서 싱글톤을 구현하는 패턴들 (멀티쓰레드 환경에서 본 관점) (0) | 2021.05.21 |
[JAVA] 메모리 누수(Memory Leak)와 GC 성능 튜닝 (0) | 2021.04.26 |
[JAVA] Blocking I/O, Non-Blocking I/O(NIO)와 대용량 트레픽 (0) | 2021.04.07 |