반응형

해당 글은 '실무로 배우는 시스템 성능 최적화' 책의 내용을 발췌한 내용입니다. 최근 해당 책을 읽다가 이전에 내가 했던 고민과 비슷한 사례에서 비롯한 성능 저하 사례가 있어 포스팅 해본다. 보통 스프링부트에서 API를 만들 때 RestTemplate을 만들어서 사용하실텐데 매번 RestTemplate객체를 new로 생성해서 사용해야하는지 싱글톤 객체로 만들어 놓고 사용해야 하는지 고민하던 때가 있었다. 처음 멋 모를 때는 API Controller마다 new로 생성해서 사용했었는데 RestTempalate가 스레드 간 공유해도 안전한 클래스라는걸 알고 난 이후에 @Bean으로 싱글톤 객체를 생성해 사용했었다. new 객체를 매번 생성할 때도 나는 아래와 같은 장애상황을 겪진 못했지만 아래와 같이 내부락이 발생할 수 있으므로 싱글톤 객체로 만들어 사용할 수 있도록 하자. 또한 @Bean 애노테이션으로 만든 싱글톤 객체보다 빈 대신 static을 사용한 싱글턴 구조를 만드는게 더 좋다고 하는데 궁금하시다면 조치부분을 참고.

스프링 프레임워크의 객체를 잘못 사용한 성능 저하 사례

현상

오픈 전 6시간 동안 진행하는 안정성 테스트 과정에서 1시간이 경과한 후 응답시간이 급격히 저하되는 현상이 발생했다.

접근 방식

오라클 JVM을 사용하고 있어 응답시간이 느려졌을 때 jstack으로 스택을 5회 수집해서 분석했다. 성능 저하 시 전체 사용자 요청을 처리 중인 스레드 중 90% 이상이 애플리케이션 내부 락 대기 중이었다.

시스템의 애플리케이션 서버는 프론트 서버와 백엔드 서버로 구성돼 있는데, 프론트 서버에 들어온 서비스 요청은 다시 HTTP를 사용해 다시 백엔드 업무 서버를 호출하는 구조다. 내부 락은 프론트 서버 내에서 백엔드 서버의 서비스를 호출할 때 사용하는 RestTemplate 객체를 생성하는 과정(RestTeamplate.init())에서 발생하고 있었다. 이에 RestTemplate 클래스에 대해 인터넷 검색을 한 결과, 애플리케이션에서 RestTemplate을 잘못 사용하고 있음을 알 수 있었다.

원인

스프링 프레임워크에서 제공하는 HTTP 클라이언트 템플릿인 RestTemplate 클래스는 객체 생성 시 내부에 무거운 Charset.availableCharsets 메서드가 실핸된다. 더구나 Charset.availableCharsets 메서드는 여러 스레드가 동시에 사용했을 때 스레드 간에 락 경합이 생겨서 급격한 성능 저하를 유발한다. HTTP로 백엔드 시스템의 서비스를 호출할 때마다 RestTemplate객체를 새로 생성해서 사용함으로써 Charset.availableCharsets 메서드에 의한 성능 저하가 발생한 것이다.

조치

RestTemplate은 스레드 간에 공유해도 안전한 클래스이므로 싱글턴 구조로 객체를 한 개만 생성한 후 스레드 간에 공유해서 사용하도록 수정한다. 그래서 객체 생성이 인스턴스마다 한 번만 이뤄져 락 경합이 제거됐다(RestTemplate 클래스를 스프링 프레임워크의 빈으로 사용할 수도 있으나 RestTemplate 클래스를 사용하는 MobileClient 클래스 또한 프레임워크의 일부라서 빈 대신 static을 사용한 싱글턴 구조를 만들었다.

결과

6시간 안정성 테스트 동안 응답시간이 증가하는 현상 없이 일정하게 유지됐다.

ref : 실무로 배우는 시스템 성능 최적화

반응형

+ Recent posts