ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이미지 압축, 해제 성능 개선하기 - Snappy 도입을 통하여
    개발/Spring Boot 2024. 5. 24. 17:10

    nGrinder를 사용하여 이미지 로드 API를 테스트하는 동안 서버가 다운되는 문제가 발생했다.

    초기에는 서버가 정상적으로 응답했지만, 가상 사용자가 점차 증가하면서 서버의 응답 속도가 느려지기 시작했다. 결국 사용자가 일정 수를 넘어서자 서버가 더 이상 요청에 응답하지 않고 다운되었다. 서버 로그를 확인해 보니 이미지 로드 API를 호출하는 중 문제가 발생하는 것을 확인할 수 있었고, 문제의 원인이 ImageUtil 클래스 내의 decompressImage 메서드를 호출하면서 발생함을 알게 되었다.

     

    문제를 찾기 위해서는 먼저, 원래 서버에서 이미지 압축과 해제가 어떻게 이루어졌는지 다시 확인하는 과정이 필요하다. 서버에서는 Deflater와 Inflater를 사용하여 이미지 압축 및 해제를 수행한다.

     

    Deflater와 Inflater는 Java의 java.util.zip 패키지에서 제공하는 클래스들로, zlib 라이브러리를 기반으로 한다. 이 클래스들은 각각 데이터 압축과 압축 해제를 지원하며, 자바 표준 라이브러리의 일부기 때문에 추가 라이브러리 없이 사용할 수 있어 편리하다.

     

    서버에서 Deflater와 Inflater를 사용했던 이유는 자바 표준 라이브러리에 일부였기 때문이다.

    public static byte[] compressImage(byte[] data) {
            Deflater deflater = new Deflater();
            deflater.setLevel(Deflater.BEST_COMPRESSION);
            deflater.setInput(data);
            deflater.finish();
    
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
            byte[] tmp = new byte[4*1024];
    
            while (!deflater.finished()) {
                int size = deflater.deflate(tmp);
                outputStream.write(tmp, 0, size);
            }
    
            try {
                outputStream.close();
            } catch (Exception ignored) {
    	//...
            }
         }

     

    public static byte[] decompressImage(byte[] data) {
            Inflater inflater = new Inflater();
            inflater.setInput(data);
            ByteArrayOutputStream outputSteam = new ByteArrayOutputStream(data.length);
            byte[] tmp = new byte[4*1024];
    
            try {
                while (!inflater.finished()) {
                    int count = inflater.inflate(tmp);
                    outputSteam.write(tmp, 0, count);
                }
                outputSteam.close();
            } catch (Exception ignored) {
            //...
    	}
      
          }

     

    코드 구현상의 문제가 없었기 때문에 무엇이 문제인지 쉽게 답을 찾기 어려웠다.

    그러나 zlib 라이브러리에 대해서 더 알아보면서, zlib는 고속 압축 알고리즘이 아니라는 사실을 알게 되었다. zlib는 DEFLATE 알고리즘을 사용하여 높은 압축률을 제공하지만, 압축 속도 측면에서는 다른 고속 압축 알고리즘에 비해 상대적으로 느렸다.

     

    zlib 라이브러리를 선택하고 사용할 때 이러한 성능 문제를 충분히 고려하지 못했던 것이다.

     

    고속 압축 알고리즘 비교 테스트에서 진행한 실험에 따르면 zlib는 다른 고속 압축 알고리즘에 비해 현저히 느린 속도를 보인다. 예를 들어, 고속 압축 알고리즘들은 1,000회의 압축 및 해제 작업을 0.5초 이내에 완료할 수 있지만, zlib는 동일한 작업에 2초 이상 걸린다. 이는 네트워크나 디스크 I/O에서 전체 성능을 저하시키는 주요 원인이 되었다.

     

    결과적으로 zlib의 성능 문제로 인해 nGrinder를 사용하여 이미지 로드 API를 테스트하는 동안 서버가 다운되는 문제가 발생했던 것이다.

     

    따라서 이러한 이유로, 문제를 해결하기 위해서는 더 빠른 압축 알고리즘으로 전환하는 것으로 고려해야 했다. 

     

    Snappy 사용하기

    Snappy는 Google에서 개발한 고속 압축 알고리즘으로, 높은 압축률보다는 빠른 압축 및 해제 속도를 목표로 설계되었다. Snappy는 실시간 데이터 압축 및 해제를 필요로 하는 애플리케이션에 매우 적합하며, CPU 사용량을 최소화하면서도 효율적인 데이터 처리를 가능하게 한다. 이는 데이터 전송 및 저장 시 성능 병목을 줄이는 데 큰 도움이 된다.

     

    public class ImageUtil {
        public static byte[] compressImage(byte[] data) throws IOException {
            return Snappy.compress(data);
        }
    
        public static byte[] decompressImage(byte[] data) throws IOException {
            return Snappy.uncompress(data);
        }
    }

     

    Snappy를 적용한 후, 이미지 로드 API의 성능이 현저히 향상되었다. Snappy의 빠른 압축 및 해제 속도로 인해 서버의 응답 속도가 크게 개선되었다. 가상 사용자가 증가하더라도 서버는 안정적으로 요청을 처리할 수 있게 되었다. 압축 및 해제 작업을 효율성이 높아지면서 서버 자원 사용이 최적화되었고, 이로 인해 서버 다운 현상이 사라졌다. Snappy를 사용함으로써 네트워크 전송 및 디스크 I/O에서 발생하던 성능 병목이 제거되었다.

     

    결론적으로, Snappy와 같은 고속 압축 알고리즘을 도입함으로써 서버의 성능을 극대화하고 안정성을 확보할 수 있었다. 이를 통해 라이브러리를 선택할 때 단순히 기능만 고려하는 것이 아니라, 성능도 함께 고려해야 한다는 중요한 교훈을 얻을 수 있었다. 앞으로는 라이브러리를 선택할 때 성능, 안정성, 그리고 실제 사용 시나리오에 대한 충분한 검토가 필요함을 깨닫게 되었다.

    댓글

Designed by Tistory.