5. nGrinder를 활용한 부하 테스트

동기

맵샷은 일정량의 메모리 여유분을 확보해야만 하는 서비스 구조를 가지고 있습니다. 이미지를 서버 메모리의 Map 자료구조에 잠시 저장하기 때문인데, 과연 해당 영역이 어느 정도의 트래픽까지 견뎌낼 수 있을 지 궁금했습니다.

진행 과정

목표

JVM을 메모리 부족으로 터트리는 것이 목표입니다.

코드 구조 상 한계점을 알아보기 위한 실험이기 때문에 스트레스 테스트에 가깝습니다.

환경 구성

코드 작성

먼저 테스트 도구로는 nGrinder를 선택했습니다. 큰 이유는 없고, Jmeter UI가 직관적이지 않아서 nGrinder를 사용하게 되었습니다. 그 다음으로 고민한 것이 서버 환경과 어떻게 테스트 환경의 메모리 구성을 일치 시킬 것인지 였습니다. 고민 끝에 다음과 같은 코드를 임시로 작성했습니다.

class TestClient {

    public void test() {

        List<String> lst = new ArrayList<>();

        for (int i = 0; i < 9; i++) {
            String uuid = UUID.randomUUID().toString();

            StorageInner storageInner = StorageInner.builder()
                    .uuid(uuid)
                    .imageByte(Base64.getDecoder().decode(TestImageByte.image))
                    .createdAt(LocalDateTime.now())
                    .build();

            storageService.add(storageInner);

            lst.add(uuid);
        }


        for (String i : lst) {
            storageService.remove(i);
        }

    }
}

맵샷의 이미지 보관 로직은 요청 하나 당 9개의 1MB 남짓한 이미지가 추가됩니다. 그래서 로컬에 1MB 이미지 파일을 텍스트로 저장 후 이용하는 방식으로 진행했습니다.

메모리 용량

현재 맵샷은 1GB 메모리의 리눅스 서버에서 300MB의 고정 힙 사이즈로 작동 중입니다. 그래서 테스트 서버 또한 힙 영역을 같은 크기로 고정해주고 진행했습니다.

1차 테스트

먼저 가상의 접속 유저 수를 나타내는 Vuser를 1 부터 천천히 늘려나갔습니다. 그 결과, 16 Vuser 까지는 그래도 큰 문제 없이 진행되었습니다.

가비지 컬렉터가 굉장히 바쁘게 작동하긴 했지만, 1 분간 8,449개의 요청을 모두 에러 없이 처리한 것을 확인할 수 있습니다.

2차 테스트

Vuser가 18명인 순간부터 힙 스페이스 에러가 뜨기 시작했습니다. 구동 30초 만에 테스트를 중단했습니다.

힙 스페이스가 가득 차서 더 이상 작업을 진행하지 못 하는 모습을 볼 수 있었습니다.

시나리오 점검

1분에 8,449개의 요청까지는 견디는 구조인 것을 확인할 수 있었습니다. 그렇다면 그 이상이 되면 서비스가 터질 수 있다는 뜻인데, 과연 코드 개선이 필요한 상황인지 점검해 보겠습니다.

23년 6월 29일자 맵샷 이용 현황입니다. 한 눈에 봐도 문제 없을 게 보이지만 조금 더 심층 분석을 해보겠습니다. 현재 맵샷에는 지도 타입이 네이버, 카카오 두 개가 존재합니다. 둘의 사용 빈도는 비슷하거나 네이버가 조금 더 높은 편이지만, 5:5라고 가정하고 진행해 보겠습니다. 이 중 네이버 지도를 이용할 경우 클라이언트 단에서 바로 가공이 되기 때문에 API 서버에는 부하가 전혀 오지 않습니다. 문제가 되는 경우는 카카오 지도를 사용했을 때인데, 현재 가장 트래픽이 높아 보이는 120명을 기준으로 잡아도 24시간 동안 60개의 요청이 서버로 온 셈입니다. 그렇다면 1분 간 보냈던 8,449개의 요청이 오려면 대략 140일이 걸립니다. 즉, 현재의 정상적인 트래픽으로는 메모리 문제로 서버가 터지기는 쉽지 않다고 생각합니다.

결론

사실 생각보다 많은 부하를 견뎌서 놀랐습니다. 코드 개선을 위해 API 서버를 거치지 않게 코드를 짜거나, SSE를 사용하여 API 서버를 일종의 터널 역할로만 사용하려는 등 몇 가지 생각해 놓았던 방안들도 있었는데, 당분간은 서버가 터질 일이 없어 보여 다행이기도 했습니다. 어렴풋이 생각만 하던 위기 상황을 수치화 시켜보니 훨씬 직관적으로 판단이 가능하다는 것을 느끼게 되었습니다.

Last updated