안녕하세요. 교육코스개발팀 이동규입니다.

저희 팀은 5월 7일부터 우아한테크코스 교육을 진행하고 있습니다. 교육과 관련하여서는 이전의 포스팅에서 모집글과, 프리코스관련 글을 확인하실 수 있습니다.

현재 우아한형제들은 다양한 DR (Developer Relations) 활동을 하고 있습니다. 우아한 테크 세미나는 우아한형제들의 DR 활동 중 하나로, 매달 다른 주제로 내외부 연사님들을 모셔 기술적 주제에 대해 살펴보는 시간으로 꾸며지며, 구성원들뿐만 아니라 같은 고민과 관심을 가진 외부 개발자분들도 초대하여 진행됩니다.

이번 5월 테크 세미나엔 자바의 신, 자바 성능 튜닝 이야기 등의 저자이신 이상민 님께서 자바, 성능, 모니터링을 주제로 강의를 진행하셨습니다.

intro

#1 자바 성능 이야기

성능의 개선 및 저하는 수익에 직접적인 영향을 줍니다. [관련 링크] 그래서 우리는 서비스가 얼마나 빠른지(Time), 일정 시간 동안 얼마나 많이 처리할 수 있는지(TPS), 그리고 얼마나 많은 사람들이 동시에 사용할 수 있는지(Users)에 대해 이야기합니다.

하지만 성능 개선에는 한계가 생길 수밖에 없기에(암달의 법칙), 부하의 원인을 파악하여 이를 제거해야 합니다. 이때, 성능을 바라보는 관점은 각자 처한 상황에 따라 달라지게 됩니다.

Users를 예로 들면, 시스템 관리자의 관점에서는 등록된 사용자와 등록되지 않은 사용자만이 존재하며, 서버의 관점에선 로그인한 사용자와 로그인하지 않은 사용자만이 존재합니다. 그리고 성능 테스터의 관점에선 사용자가 Concurrent User 인지 Active User 인지가 중요합니다. 여기서 Concurrent User란, 웹 페이지를 띄어놓은 사용자처럼, 언제든지 부하를 줄 수 있는 사용자를 의미합니다. 반면, Active User는 메뉴나 링크를 누르고 결과가 나오기를 기다리는 등 실제로 서버에 부하를 주고 있는 사용자를 의미합니다. Active User 와 Concurrent User 의 비율은 서비스의 성격에 따라 다르므로 이 점을 감안하고 성능테스트를 계획해야 합니다. (성능 테스트시에 VUser는 Active User와 유사합니다.) 가령 수강신청의 경우, 특정 시간대엔 그 비율이 90%에 육박할 수 있어, 전체 평균을 기준으로 테스트할 경우 잘못된 판단을 이끌어낼 수 있습니다.

시간(Time) 역시 바라보는 관점에 따라 개념이 달라집니다. 사용자에게 있어서 Time은 응답시간만 존재합니다. 하지만 실제 시스템 입장에선, 사용자가 요청에 대해서 응답을 받은 후에 웹 페이지를 보는 등의 작업을 하는 시간(Think Time)이 존재합니다.

usertime

성능 테스트 시엔 실제 지연시간이 발생하는 구간을 파악하여야 합니다. 가령 Server 구간에서 발생한 경우, DB와 애플리케이션 간 연결의 문제, 프로그램 로직 상의 문제 혹은 서버의 리소스 부족 등을 의심해 볼 수 있습니다. 또한 네트워크 이슈의 경우 테스트하는 환경에 따라 달라질 수도 있습니다. 지연 현상은 사용자의 이탈과 매우 밀접하기에 개선되어야 하지만, 단순히 서버를 늘린다고(Scale out) 해결되는 것은 아닙니다. 이에 출시 전에 테스트를 하여 최대 응답시간을 파악하고 있어야 하며, 상위 5%의 화면이 95% 사용자 요청을 받는다는 점을 감안하고 튜닝의 대상을 선정해가야 합니다.

Time과 달리, TPS (Transaction Per Seconds)는 Scale out 혹은 Scale up을 통해 증가시킬 수 있습니다. 보통 테스트 시에 단순히 응답시간을 기준으로 종료시키진 않고, TPS나 DB Connection, CPU 등을 종합적으로 확인하고 중단시킵니다.

tpsusertime

User 증가 시 TPS는 어느 정도 증가하다가 더 이상 증가하지 않게 되며, Time은 일정하게 유지되다 점차적으로 증가합니다. 반면, 부하가 증가할 경우(TPS가 증가) 지연시간은 변곡점에 이르기도 하는데, 이 경우 시스템 리소스가 누수되고 있는 것은 아닌지 확인해봐야 합니다. 일반적으로 병목은 DB Connection, Web page 혹은 Network에서 발생하지만, 성능 튜닝의 목표 설정은 경험에 의존하기보단 데이터에 근거해 판단하여야 합니다. 추가적으로, 최적값을 설정하기 위해서는 해당 영역에 대한 깊이 있는 이해가 필요하며, 관련 전문가를 찾아가 문의하는 것도 좋습니다. (DBA와 친해지세요)

gatling

성능을 테스트하기 위한 도구로는 HP의 상용 도구인 LoadRunner, JMeter, nGrinder, 그리고 Gatling 등이 있습니다. 이 중 Gatling의 경우 Jenkins Plugin이 제공되어 CI/CD pipeline에 쉽게 적용할 수 있는 점, Event와 Async IO 기반으로 JMeter, nGrinder 대비 성능상 이점이 있다는 점 [관련 링크] 등이 인상적이었습니다. [JIRA Performance Testing]

#2 자바 이야기

자바 라이선스에 대한 이야기로 2번째 세션이 시작되었습니다. Java 8은 공식 홈페이지에서 받아서 쓰면 문제가 없으며, Java 11부터는 회사에서 사용하게 될 경우 라이선스 문제가 복잡해질 여지가 있습니다. 맥북에서 개발용으로 사용하는 것은 괜찮지만, 배포본의 경우 회사 직원 수 혹은 서버별 과금이 발생할 수 있습니다. 따라서 openjdk를 사용하는 것도 검토해봐야 하며, 과금에 대한 자세한 부분은 공식 문서를 확인해보시길 바랍니다. 추가적으로, Java 버전에 대한 상세한 내용은 링크를 참조하시길 바랍니다.

Java8이 등장하면서 Lambda, Stream, Optional, Default method, LocalDate / LocalTime 등이 추가되었습니다.

  • Lambda 표현식과 Stream 인터페이스의 경우 남용하면 오히려 가독성을 떨어트릴 수 있습니다.
  • for/foreach/stream 성능 비교에 대한 질문이 있었는데요, 현재 성능 차이는 거의 없다고 합니다.
  • ParallelStream은 CPU core 개수만큼 스레드를 사용하며 내부적으로 common fork join pool을 사용합니다. 이에 fork join pool을 사용하는 다른 thread에 영향을 줄 수도 받을 수도 있습니다. 따라서 운영환경에서는 주의해서 사용하여야 합니다.

Java9의 주요 특징으로는 Compact Strings, G1 default GC가 있으며 그 외에도 Collections, JShell 등이 등장하였습니다.

tps

  • 웹 애플리케이션의 경우 String 클래스를 많이 사용하기 때문에, Java 버전을 올려주는 것만으로도 Compact Strings로 인한 성능 개선 효과를 얻을 수 있습니다. 기존에 String class는 char[]을 감싼 형태로 UTF-16기반의 2byte를 참조하였지만, Compact Strings는 byte[]을 채택함으로써 문자열에 따라 Latin-1(1byte) 혹은 UTF-16(2byte)로 인코딩된 문자를 저장하기에 메모리 공간 효율이 높아지고 GC 발생이 적어지는 효과가 있습니다.

Java10에서는 var가 등장하였고, Java11부터는 Oracle JDK가 유료화되었으며 HTTP2, 웹소켓 등을 쉽게 사용할 수 있는 java.net.http.HttpClient가 제공됩니다. [관련 링크] 그리고 Java12의 특징으로는 Switch expressions, JMH 내장, Shenandoah gc가 있습니다.

추가적으로, AdoptOpenJdk에 대해서 자세히 설명을 들을 수 있었습니다. AdoptOpenJdk는 Open source로, 600명 이상의 자원봉사자로 구성된 Commnunity가 주도하고 있으며, 별도의 제품이 아니라 IBM의 내부 자바 테스팅 인프라 등을 활용한 build farm에서 빌드를 해서 제공해줍니다. (CI/CD 지원을 의미)

#3 자바 모니터링 이야기

APM의 동작 방식을 이해하기 위해서는 BCI(Byte Code Instrumentation)에 대해 이야기해야 합니다. BCI란 런타임이나 로드 시에 클래스의 바이트 코드에 변경을 가하는 기법으로, 소스파일의 수정 없이 원하는 기능을 부여할 수 있고 필요한 정보를 추적할 수 있습니다. Spring AOP 역시 BCI를 사용하고 있습니다. 관련하여 java.lang.instrument pacakge와 ClassFileTransformer interface 문서를 확인해보시길 권장합니다. [관련 링크] BCI 도구로는 대표적으로 ASM, BCEL, Javassist가 있으며 제니퍼의 경우 BCEL을, 핀포인트의 경우 Javassist를 사용한다고 합니다.

추가적으로, APM에서 서버 간 호출 관계를 어떻게 추적하는지에 대한 이야기가 이어졌습니다. 핀포인트의 경우, 서버로부터 요청이 오면 Http header에서 caller를 확인하고 caller가 없으면 자신을 caller로 등록합니다. 그리고 다른 서버에 요청할 때 자신이 생성한 id를 Http header에 넘겨주는데, 이런 연결들이 이루어져 관계가 구성되고 분산 추적이 가능해집니다. [관련 링크] 따라서 소켓 통신의 경우 패킷을 나누기 때문에 헤더를 추적하는 방식으로는 분석이 불가능합니다.

cputps

상용 APM은 대표적으로, Dynatrace, New relic, AppDynamics, WhaTap 등이 있으며, 오픈소스로는 Scouter, Pinpoint 등이 있습니다. 이 중 Scouter와 Pinpoint를 비교해보면, Scouter는 실시간 모니터링, 트러블슈팅을 위한 지표가 많고 Pinpoint의 경우 전체 뷰 등 서버 간 관계를 지켜볼 수 있고 분산 저장이 가능하다는 이점이 있습니다. Scouter로 모니터링할 경우에는 CPU, TPS, Active Service EQ, GC time, GC count, Xlog 등의 지표를 구성할 수 있습니다. 이 중 CPU와 TPS는 기간별 trends를 확인할 수 있으며, Active Service EQ는 시각적으로 서비스의 상태를 확인하고 Thread 목록과 StackTrace 목록을 찾아가기 쉽게 구성되어 있어, 문제가 되는 서버를 추적하기가 용이합니다. Scouter에는 이 외에도 알람, 성능 카운터, Summary, Xlog data 전처리, Xlog 상세 profile 정보 전처리 등을 위한 Server 측의 플러그인을 제공하며 Agent 측에서는 httpservice, httpcall, capture, jdbcpull 등의 플러그인을 제공하고 있습니다. 이 중 capture.plug의 경우 특정 메서드를 지정하면 해당 메서드가 호출되었을 때 리턴 값을 확인할 수 있다고 합니다. 추가적으로 Scouter 설정값과 관련하여서는 Server와 Agent 모두 Configure Class에서 확인이 가능합니다. [관련 링크]

그리고 Scouter가 현재 커미터를 모집 중이니 많은 참여 바랍니다.

# QnA

  • java null에 대한 질문: 관련자료 [관련 링크]

  • GC 튜닝에 대한 질문

    GC 튜닝은 가장 마지막에 하는 것이라고 합니다. 왜 애플리케이션이 메모리를 많이 사용하는지, 왜 임시 객체를 많이 사용하는지를 먼저 확인하여야 하며, Thread와 DB connection pool을 최적화하는 것도 중요합니다. DB Connection Pool의 경우 기본이 10개라, 그 이상 들어오면 대기큐에 들어가고, 이에 Connection Time이 길어질 수도 있다고 합니다. 또한 서비스 규모에 따라 로그가 시스템에 큰 영향을 미칠 수도 있습니다. 최근의 stack trace를 보면 여러 줄이 뒤죽박죽 섞여있지 않은 것을 확인할 수 있는데요. 로그를 작성하기 위해 락을 걸어 성능에 영향을 줄 수 있습니다. 따라서 로그는 꼭 필요한 것만 작성하도록 하여야 합니다. GC 옵션을 결정하는 것과 관련해서는 직접 화이트보드에 쓰시면서 설명해주셨는데요. 절대적인 GC 옵션을 지정하는 것은 어렵고 메모리를 늘려가며 Full GC time을 측정한 후 서비스 특성을 고려하여 결정해야 한다고 하셨습니다.

  • JVM이 비정상적으로 죽은 경우에 대한 질문

    JVM이 비정상적으로 죽으면 hs_err로 시작하는 파일이 java project를 실행하는 폴더 안에 생기므로 이를 분석해야 한다고 합니다.

  • APM으로 인한 성능 저하에 대한 질문

    Dynatrace는 성능 저하가 5%나면 Collector가 분석하는 데이터량을 줄인다고 합니다. 하지만 오픈소스를 사용하게 될 경우, 성능 저하가 얼마나 나는지는 직접 비교해봐야 합니다. 모니터링하는 서버와 하지 않는 서버의 CPU를 비교하고, 많이 차이 날 경우엔 샘플링하는 것을 고려해야 합니다.

마지막으로, 절대 단정 짓지 말고 직접 확인하여 데이터로 이야기해야 한다는 점을 거듭 강조하셨습니다.

# 개인적인 소감

‘자바의 신’을 기본서로 시작하여, ‘자바 성능 튜닝 이야기’로 면접을 준비하였고, 지금은 ‘자바 개발자와 시스템 운영자를 위한 트러블 슈팅 이야기’를 읽고 있습니다. 이 세미나를 통해 성능에 대한 깊이 있는 이해를 얻겠다는 생각보단, 팬심으로 지원했던 것 같습니다. 하지만 기대했던 것보다 많은 영감을 받을 수 있었고, 흥미가 가는 키워드도 많이 얻어 갈 수 있었습니다. 특히 (여기엔 다룰 수 없었지만) 여러 가지 사례들을 통해 지표를 바라보는 관점들을 엿볼 수 있어서 너무 좋았습니다.

우아한 테크세미나는 매월 진행되고 있습니다. 다음 세미나 역시 매우 매력적인 연사님께서 준비하고 계시다고 하니, 이번에 못 오신 분들은 다음 세미나를 노려보시는 것도 좋을 거 같습니다.