톰캣 튜닝

SERVER/TOMCAT 2017. 12. 6. 16:32
728x90
반응형

1. JVM 옵션


힙 사이즈 크기

New 영역

XX:NewRation

XX:NewSize


종류 

옵션 

동작모드

-server

전체 힙 크기

-Xms와 - Xmx의 값을 동일하게 설정 

New 영역 크기 

-XX:NewRation 2~4 정도의 값

Perm 크기 

-XX:NewSize

-XX:MaxNewSize

-XX:PermSize=256m

-XX:MaxPermSize=256m

성능에 영향을 미치지 않으므로 동작에 문제가 없을 정도만 지정한다  

GC 로그 

-Xloggc:$CATALINA_HOME/logs/gc.log 

-XX:+PrintGCDetails

-XX:+PrintGCDateStamps

GC 로그를 남기는 것은 특별히 Java 애플리케이션 수행 성능에 영향을 미치지 않는다. 가급적이면 GC로그를 남기는 것이 좋다.

GC 알고리즘 

-XX+UserParNewGC

-XX:+CMSParallelRemarkEnabled

-XX:+UseConcMarkSweepGC

-XX:CMSInitiaingOccupancyFraction=75

OOM 에러 발생 시 힙 덤프 생성 

-XX:+HeapDumpOnOutOfMemoryError

-XX:HeapDumpPath=$CATALINA_HOME/logs

OOM 발생 이후 조치 

-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh

-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh

힙 덤프를 남긴 뒤, 관리 정책에 맞게 적합한 동작을 취할 수 있도록 한다.

 


- server

제일 먼저 해야할일은 JVM 모드를 server 모드로 전환하는 것이다. JVM 내의 핫스팟 컴파일러도 클라이언트 애플리케이션이나 서버 애플리케이션이냐에 따라서 최적화 되는 방법이 다르다.

그리고 메모리 배치 역시 클라이언트 애플리케이션의 경우 버튼이나 메뉴는 한 번 메모리에 로드되면, 애플리케이션이 끝날 때까지 메모리에 잔존하기 때문에 OLD 영역이 커야 하지만, 서버의 경우 REQUEST를 받아서 처리하고 응답을 주고 빠져서 소멸되는 객체들이 대부분이기 때문에, NEW 영역이 커야 한다. 애플리케이션이 서버냐 클라이언트냐에 따라 최적화 옵션이 자동으로 적용되기 때문에, 반드시 적용하기를 바란다.


- 메모리 옵션

JVM 튜닝의 대부분이 메모리 튜닝이고, 그중에서도 JVM 메모리 튜닝은 매우 중요하다. 결국 FULL GC 시간을 줄이는 것이 관건인데, 큰 요구 사항만 없다면, 전체 HEAP SIZE는 1G정도가 적당하다. 그리고 NEW대 OLD의 비율은 서버 애플리케이션의 경우 1:2 비율이 가장 적절하다. 

PermSize는 클래스가 로딩되는 공간인데, 배포하고자 하는 애플리케이션이 아주 크지 않다면 128m 정도가 적당하다. 보통 256m을 넘지 않는다. 256m이 넘는다면 애플리케이션 배포나 패키징에 문제가 있다고 봐야 한다.

힙 사이즈는 jvm에서 자동으로 늘리거나 줄일 수가 없으므로 -Xms와 -Xmx로 최소, 최대 힙 사이즈를 정할 수 있는데, 서버 시스템의 경우 항상 최대 사용 메모리로 잡아 놓는 것이 좋다. 메모리가 늘어난다는 것은 부하가 늘어난다는 것이고, 부하가 늘어날때 메모리를 늘리는 작업 자체가 새로운 부하가 될 수 있기 때문에, 같은 값을 사용하는 것이 좋다.


-Xmx1024m –Xms1024m -XX:MaxNewSize=384m -XX:MaxPermSize=128m


이렇게 하면 전체 메모리 사용량은 힙 1024m, new 384m, perm 128m이 되고 JVM 자체가 사용하는 메모리가 보통 300~500 내외가 되서 자바 프로세스가 사용하는 메모리 량은 대략 1024+128+300~500 으므로 대략 1.5g 정도가 된다.


32bit JVM의 경우 프로세스가 사용할 수 있는 공간은 4G가 되는데, 이중 2G는 시스템이 사용하고 2G가 사용자가 사용할 수 있다. 그래서 위의 설정을 사용하면 32BIT JVM에서도 잘 동작한다.

64BIT JVM의 경우 더 큰 메모리 영역을 사용할 수 있는데, 일반적으로 2G를 안 넘는 것이 좋다. 2G가 넘어서면 FULL GC 시간이 많이 걸리기 시작하기 때문에, 그다지 권장하지 않는다. 시스템의 가용 메모리가 많다면 HEAP을 넉넉히 잡는 것보다는 톰캣 인스턴스를 여러개 띄워서 클러스터링이나 로드밸런서로 묶는 방법을 권장한다.


<애플리케이션 성능 측정>

애플리케이션 성능을 측정하기 위해 파악해야 할 정보는 다음과 같다.

- TPS(Transaction Per Second)/Ops(Operation Per Second)

- RPS(Request Per Second) - 사용자 응답 시간


<구현의 문제>

프로파일러나 힙 덤프 결과를 통해 힙을 차지하고 있는 객체의 종류와 생성 개수를 확인해보고 적합여부를 판단한다. 불필요한 객체가 많이 생성되어 있다면 코드를 수정하는 것이 좋다. 


<CPU 사용률이 낮다>

TPS가 낮은데 CPU 사용률도 낮다면 blocking time이 원인이다. 이 경우 연동 시스템의 문제나 동시성 문제일 수 있다. 덤프 결과 분석이나 프로파일러를 이용해 확인할 수 있다. 상용 프로파일러를 이용하면 매우 정밀한 lock 분석을 할 수 있지만 대부분의 경우 jvisualvm에 있는 CPU 분석만으로도 충분한 결과를 얻을 수 있다.


<CPU 사용률이 높다>

TPS가 낮은데 CPU 사용률이 높다면 효율적이지 못한 구현 때문일 가능성이 높다. 이 경우 프로파일러를 이용한 병목 지점 파악이 유효하다. jvisualvm이나 eclipse의 TPTP, JProbe 등을 이용해 분석하자.


<튜닝 접근 방법>




2. 톰캣 튜닝


애플리케이션 관점에서의 튜닝도 중요하지만, 각 솔루션에 대한 특성을 업무 시나리오에 맞춰서 튜닝하는 것도 못지 않게 중요하다. 여기서 톰캣 튜닝을 설명하는 것은 톰캣 자체에 대한 튜닝 옵션을 소개하는 것도 목적이 있지만, 그보다 업무형태에 따라서 어떠한 접근을 해서 톰캣을 튜닝하는지를 소개하기 위함이다.

 

가정

여기서 튜닝 하는 톰캣은 HTTP/JSON형태의 REST 형태로 서비스를 제공하는 API 서버의 형태이다. 여러대의 톰캣을 이용하여 REST 서비스를 제공하며, 앞단에는 L4 스위치를 둬서 부하를 분산하며, 서비스는 stateless 서비스로 공유되는 상태 정보가 없다. 


1. Listener


<Listener className="org.apache.catalina.security.SecurityListener" checkedOsUsers="root" />


톰캣 시작시 root로 실행하는 것을 방지하는 부분이다. 톰캣 단독으로 80포트 이용시에는 필요없을 부분이긴 하지만, 그렇지 않다면 해당 옵션을 켜주는게 좋다. 구문은 주석처리되어 있으므로 주석을 해제해야 한다.


2. Connector


protocol=”org.apache.coyote.http11.Http11Protocol”


저 protocol setting인데, Tomcat은 네트워크 통신하는 부분에 대해서 3가지 정도의 옵션을 제공한다. BIO,NIO,APR 3 가지이다. NIO는 Java의 NIO 라이브러리를 사용하는 모듈이고, APR은 Apache Web Server의 io module을 사용한다. 그래서 C라이브러리를 JNI 인터페이스를 통해서 로딩해서 사용하는데, 속도는 APR이 가장 빠른것으로 알려져 있지만, JNI를 사용하는 특성상, JNI 코드 쪽에서 문제가 생기면, 자바 프로세스 자체가 core dump를 내면서 죽어 버리기 때문에 안정성 측면에서는 BIO나 NIO보다 낮다. BIO는 일반적인 Java Socket IO 모듈을 사용하는데, 이론적으로 보면 APR > NIO > BIO 순으로 성능이 빠르지만, 실제 테스트 해보면 OS 설정이나 자바 버전에 따라서 차이가 있다. Default는 BIO이다.



  1) connectionTimeout="5000"
   타임아웃 시간을 지정한다. 연결 요청후 URI 요청이 들어오기까지의 대기시간이다. 기본값은 60초이며
   타임아웃 시간을 10초 이내로 지정하는게 좋다.(5000은 5초 이다)


   2) minSpareThreads="25"
   Tomcat이 실행될때 생성되는 스레드 사이즈 이다. 


   3) maxThreads="100"
   Tomcat의 최대 스레드 수를 지정한다. 최대 접속가능한 Active User의 수를 뜻한다. 

   일반적으로 100 내외가 적절하고 트랜젝션의 무게에 따라 50~500개 정도로 설정하는게 일반적이다. 이 값은 성능 테스트를 통해서 튜닝을 하면서 조정해 나가는 것이 좋다.


   4) acceptCount="100" / 10
   Tomcat의 스레드가 full 일 경우 요청을 대기하는 queue의 길이이다. 

   순간적인 과부화 상황에 대비하기 위해 큐의 길이을 10내외 정도로 짧게 주는게 좋다고 한다.
   이 옵션은 request Quere의 길이를 정의한다. HTTP Request가 들어왔을 때, idle thread가 없으면 queue에서 idle Thread가 생길때까지 요청을 대기하는 queue의 길이이다. 보통 queue에 메세지가 쌓였다는 것은 해당 톰캣 인스턴스가 처리할 수 있는 쓰레드가 없다는 이야기이고, 모든 쓰레드를 사용해도 요청을 처리를 못한다는 것인 이미 장애 상태일 가능성이 높다. 그래서 큐의 길이를 길게 주는 것보다는, 짧게 줘서, 요청을 처리할 수 없는 상황이면 빨리 에러 코드를 클라이언트에게 보내서 에러처리를 하도록 하는 것이 좋다. Queue의 길이가 길면, 대기 하는 시간이 길어지기 때문에 장애 상황에서도 계속 응답을 대기를 하다가 다른 장애로 전파 되는 경우가 있다. 순간적인 과부하 상황에 대비하기 위해서 큐의 길이를 0보다는 10내외 정도롤 짧게 주는 것이 좋다.

   5) disableUploadTimeout="true"
   데이터를 업로드할 시에 connectionTimeout을 끌것인지에 대한 내용이다. 

   데이터가 큰 경우 업로드 시간이 connectionTimeout을 넘는경우가 많으므로 설정하는게 좋다.


   6) maxConnection="8192"
   Tomcat이 유지할수 있는 커넥션 최대 수이다. 

   하지만 현재 연결되어 있는 실제 커넥션의 최대 수는 아니다.(TIME_WAIT 등과 같은 커넥션 존재)

   이 수는 현재 연결되어 있는 실제 커넥션의 수가 아니라 현재 사용중인 socket fd의 수이다. TCP 커넥션 특성상 커넥션이 끝난 후에도 바로 소켓이 클로즈되는 것이 아니라 FIN 신호를 보내고, TIME_WAIT 시간을 거쳐서 커넥션을 정리한다. 실제 톰캣 인스턴스가 100개의 커넥션으로부터 들어오는 요청을 동시 처리할 수 있다 하더라도, 요청을 처리하고 소켓이 클로즈되면 TIME_WAIT에 머물러 있는 커넥션 수가 많기 때문에, 단시간내에 많은 요청을 처리하게 되면 TIME_WAIT가 사용하는 FD수 때문에, maxConnection이 모자를 수 있다. 그래서 maxConnection은 넉넉하게 주는 것이 좋다. 이외에도 http 1.1 keep alive를 사용하게 되면 요청을 처리하지 않는 커넥션도 계속 유지되기 때문에, 요청 처리수보다 실제 연결되어 있는 커넥션수가 높게 된다. 그리고 프로세스 당 열 수 있는 fd수는 ulimit -f를 통해서 설정이 된다. maxconnection을 8192로 주더라도, ulimit -f에서 fd수를 적게 해놓으면 소용이 없기 때문에 반드시 ulimit -f로 최대 물리 커넥션 수를 설정해놔야 한다.


 7) maxKeepAliveReuqest="1"

HTTP 1.1. Keep Alive Connection을 사용할 때, 최대 유지할 커넥션 수를 결정하는 옵션이다. rest 방식에서 keep alive 를 사용하지 않기 위해서 값을 1로 준다.

만약에 keepalive를 사용할 예정이라면, maxconnection과 같이 ulimit에서 fd수를 충분히 지정해야 한다.


   7) enableLookups=”false”
   Tomcat이 실행되는 Servlet/JSP 코드 중에서 들어오는 http request에 대한 ip를 조회하는 명령등이 있을 경우, 톰캣은 DNS이름을 IP주소로 바뀌기 위해서 DNS 서버에 룩업 요청을 보낸다. 이것이 HTTP REQUST 처리 중에 일어나는데, 다른 서버로 DNS 쿼리를 보낸다는 소리이다. 그만큼의 서버간의 round trip 시간이 발생하는데, 이 옵션을 false로 놓으면 dns lookup 없이 그냥 dns 명을 리턴하기 때문에, round trip 발생을 막을 수 있다.


   8) compression="off"

   HTTP Message body를 gzip 형태로 압축해서 리턴한다. 업무 시나리오가 이미지나 파일을 response하는 경우에는 compression을 적용함으로써 네트워크 대역폭을 절약하는 효과가 있겠지만, JSON/REST 통신은 굳이 compression을 사용할 필요가 없으며, compression에 사용되는 CPU를 차라리 비즈니스 로직 처리에 사용하는 것이 더 효율적이다.


  

 9) tcpNoDelay="true"

TCP 프로토콜은 기본적으로 패킷을 보낼때 바로 보내지 않는다. 작은 패킷들을 모아서 버퍼 사이즈가 다 차면 모아서 보내는 로직을 사용한다. 그래서 버퍼가 4K라고 가정할 때, 보내고자 하는 패킷이 1K이면 3K가 찰 때까지 기다리기 때문에, 바로바로 전송이 되지 않고 대기가 발생한다. tcpNoDelay 옵션을 사용하면, 버퍼가 차기전에라도 바로 전송이 되기 때문에 전송 속도가 빨라진다. 반대로, 작은 패킷을 여러번 보내기 때문에 전체적인 네트워크 트래픽은 증가한다. (예전에는 대역폭이 낮아서 한꺼번에 보내는 방식이 선호되었지만 요즘은 망 속도가 워낙 좋아서 tcpnodelay를 사용해도 대역폭에 대한 문제가 크지 않다.

[출처] 톰캣 튜닝|작성자 alcinoe


728x90
반응형
블로그 이미지

nineDeveloper

안녕하세요 현직 개발자 입니다 ~ 빠르게 변화하는 세상에 뒤쳐지지 않도록 우리모두 열심히 공부합시다 ~! 개발공부는 넘나 재미있는 것~!

,