저는 실제로 배포를 해보며 사용자 입장에서 기능을 생각해보고, 피드백을 받아보면서 기능을 추가하는 것 뿐만아니라 성능적인 부분도 향상시키고 싶다는 생각이 들었고 성능의 지표를 확인하기 위해 jmeter, k6 와 같은 성능 테스트 도구를 활용하기로 하였습니다.
측정 하기위한 지표는 다음과 같습니다.
코드를 짤때 성능적인 부분을 고려한다고 했지만 아키텍처 적으로도, Application 코드로도 충분히 개선시킬만한 부분이 존재한다고 생각합니다.
지금 이 블로그를 최초 작성할때 당시의 성능을 측정 후 어떻게 더 발전시켜 나가는지 과정을 남겨보며 어떤점들을 개선했는지 기록하고자 합니다.
Jmeter 사용법은 구글링을 좀만하면 찾아볼 수 있고 이 게시글의 주 내용이 아니기때문에 생략하겠습니다.
간단하게 프로젝트 아키텍처를 작성해봤습니다. 우리가 테스트 해볼건 Study Server에 Rest API 요청을 보낼 것 입니다.
비교 대상
1. GCE에 있는 APIGateway를 통해 Study Server에 요청
2. Study Server 에 다이렉트로 요청 (GKE 의 externalIp로 바로 요청)
3. 기존의 모놀리식의 홈페이지 로그인 창
쓰레드 구성은 다음과 같습니다.
위 쓰레드 요청을 5회 반복 한 결과
위에도 언급했지만 1,2 번은 동일한 api에 대한 요청이지만 3번은 요청 내용이 다릅니다.
Throughput 을 TPS 라고 생각하고 정의 하고 보기 편하게 테이블로 비교하겠습니다.
평균 응답시간 | 지연시간 | 성공률 | TPS | |
---|---|---|---|---|
Gateway 요청 | 1096 | 87~3800 | 99.9% | 15.3/sec |
바로 API요청 | 1192 | 120~4000 | 100% | 15.3/sec |
Monolithic | 91 | 13~860 | 100% | 15.3/sec |
TPS를 확인해보면 최초 200회 요청 시 약 30정도 나오다가 요청이 많아 질 수록 낮아지는 모습이 식별됐습니다.
이번엔 200명의 사용자가 1회씩 요청 하는 경우 1회 요청으로 테스트
Gateway
다이렉트 요청
Monolithic
평균 응답시간 | 지연시간 | 성공률 | TPS | |
---|---|---|---|---|
Gateway 요청 | 1742 | 168~5200 | 92% | 34.8/sec |
바로 API요청 | 1547 | 120~4000 | 100% | 32.5/sec |
Monolithic | 65 | 22~420 | 100% | 35.9/sec |
약간의 변화는 있지만 큰 이변은 없는 모습입니다.
어떻게 보면 Gateway 가 요청 보내는곳과 다이렉트로 연결 하는곳이 같은 처리를 해서 TPS가 낮게 나오는 것이 아닌가 라는 생각이 들어서 다시 테스트 해봤습니다.
200요청을 1번씩 보내는 것으로 1회 테스트
Gateway
다이렉트
평균 응답시간 | 지연시간 | 성공률 | TPS | |
---|---|---|---|---|
Gateway 요청 | 1423 | 83~2200 | 100% | 69.4/sec |
바로 API요청 | 951 | 51~2100 | 100% | 73.2/sec |
같은 API에 요청을 보내는데 한번을 보내는 실수로 잘 못된 테스트 값이 나왔었습니다.
하지만 평균 응답시간이 많은 차이를 보이고 있고 TPS도 낮다고 생각이 듭니다.
불필요 log 삭제 및 logback vs log4j2
@Override
public GatewayFilter apply(Config config) {
return (((exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("[글로벌 필터] REQUEST >>> IP : {}, URI : {}", request.getRemoteAddress().getAddress(), request.getURI());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("[글로벌 필터] RESPONSE >>> IP : {}, URI : {}, Status : {}",
request.getRemoteAddress().getAddress(),
request.getURI(),
response.getStatusCode()
);
}));
}));
}
Gateway의 filter 부분입니다. Spring cloud gateway 는 webflux로 비동기 처리가 이루어지는 데 log를 보면 @Slf4j 를 사용하고 있습니다. 따라서 구현체인 logback 을 사용하는 것인데요 logback 은 log4j2 에 비해 비동기 처리시 성능저하를 발생 시킬 수 있습니다. 물론 asyncappender 설정을 하지 않은 탓 도 있지만 logback vs log4j2 를 다루기 전에 원인이 로그에 있는지 확인 을 해봤습니다.
200 회 1회 요청 테스트
Gateway
평균 응답시간 | 지연시간 | 성공률 | TPS | |
---|---|---|---|---|
AS-IS | 1423 | 83~2200 | 100% | 69.4/sec |
TO-BE | 1086 | 143~2000 | 100% | 73.4/sec |
약간의 성능에 영향을 미치는것을 확인 할 수 있었습니다.