준비 요청 구성으로 성능 향상

준비 요청을 사용하면 앱 코드가 새로 생성된 인스턴스에 로드되는 동안 요청 및 응답의 지연 시간을 줄일 수 있습니다.

App Engine이 새 인스턴스에 앱 코드를 로드해야 하는 경우가 종종 있습니다. 다음과 같은 상황에서 인스턴스를 로드할 수 있습니다.

  • 특정 버전의 앱을 다시 배포하는 경우
  • 요청의 부하가 현재 실행 중인 인스턴스 집합의 용량을 초과해 새 인스턴스가 생성되는 경우
  • 기본 인프라나 실제 하드웨어를 유지보수 및 수리하는 경우

앱 코드를 새 인스턴스에 로드하면 로드 요청이 발생할 수 있습니다. 로드 요청으로 인해 사용자의 요청 지연 시간이 증가할 수 있으나 준비 요청을 사용하면 이러한 지연 시간을 방지할 수 있습니다. 준비 요청은 실시간 요청이 새 인스턴스에 도달하기 전에 앱 코드를 이 인스턴스에 로드합니다.

애플리케이션에 준비 요청을 사용 설정하면 App Engine이 애플리케이션에 새 인스턴스가 필요한 시점을 감지하고 준비 요청을 시작하여 새 인스턴스를 초기화합니다. 하지만 이러한 감지 시도가 항상 성공하는 것은 아닙니다. 따라서 앱에 준비 요청이 사용 설정되어 있더라도 로드 요청이 발생할 수 있습니다. 예를 들어 앱에서 어떠한 트래픽도 처리하고 있지 않으면 앱에 대한 최초 요청은 항상 준비 요청이 아닌 로드 요청이 됩니다.

준비 요청은 App Engine 애플리케이션에 대한 다른 요청과 마찬가지로 인스턴스 시간을 사용합니다. 준비 요청을 사용 설정한 경우 대부분 애플리케이션은 로드 요청이 아닌 준비 요청에서 초기화되므로 인스턴스 시간이 거의 증가하지 않습니다. 그러나 준비 요청 중에 사전 캐싱과 같은 추가 작업을 수행하면 인스턴스 시간 사용량이 증가할 수 있습니다. min_idle_instances0보다 높게 설정하면 인스턴스가 처음 시작될 때 준비 요청이 발생할 수 있지만 그 후에도 인스턴스를 계속 사용할 수 있습니다.

기본 준비 요청은 모든 JAR 파일의 색인이 메모리에 생성되도록 하고 애플리케이션과 필터를 초기화합니다.

준비 요청 사용 설정

준비 요청은 사용자가 제공한 구성을 토대로 인스턴스 자동 확장을 제어하는 App Engine 스케줄러에서 사용됩니다. App Engine 자바 런타임에서 준비 요청은 기본적으로 사용 설정되므로 App Engine이 GET 요청을 /_ah/warmup으로 보내면 요청에 따라 애플리케이션 코드를 응답하고 초기화할 수 있습니다. 다음 방법 중 하나를 사용하여 준비 요청에 응답할 수 있습니다.

<load-on-startup> 서블릿 사용
준비 로직을 제공하는 가장 쉬운 방법은 web.xml 구성 파일에서 자체 서블릿을 <load-on-startup>으로 표시하는 것입니다.
ServletContextListener 사용
준비 요청이나 로드 요청을 통해 서블릿을 먼저 호출하기 전에 커스텀 로직을 실행할 수 있습니다.
커스텀 준비 서블릿 사용
커스텀 준비 서블릿을 사용하면 요청 로드 시가 아닌 준비 요청 중에 서블릿의 service 메서드가 호출됩니다.

선택한 방법에 따라 /_ah/warmup에 대한 고유한 핸들러를 구현해야 할 수도 있습니다.

시작하기 전에

준비 요청을 사용 설정한 경우, 스케줄러는 인스턴스가 더 많이 필요하다고 판단하면 인스턴스를 시작합니다. 스케줄러는 준비 요청을 사용하여 앱을 시작하므로, 앱이 준비 요청을 처리하지 않더라도 로그에 준비 요청이 표시됩니다.

다음 메시지는 /_ah/warmup 로그의 준비 요청을 보여줍니다.

This request caused a new process to be started for your application, and thus caused your application code to be loaded for the first time. This request may thus take longer and use more CPU than a typical request for your application.

준비 요청이 항상 호출되는 것은 아닙니다. 예를 들어 첫 번째로 시작되는 인스턴스이거나 트래픽 양이 갑자기 증가하는 등의 일부 경우에는 로드 요청이 대신 전송됩니다. 하지만 준비 요청이 사용 설정된 경우에는 이미 준비된 인스턴스에 요청을 보내는 것이 '최고의 방법'입니다.

자바 8에서는 기본적으로 준비 요청이 사용 설정됩니다. 준비 요청을 사용 설정하려면 appengine-web.xml에서 - warmupinbound_services 지시문에 추가합니다. 기본적으로 준비는 사용 설정되므로 이전에 appengine-web.xml에서 준비 요청을 중지한 상태로 애플리케이션을 배포한 경우에만 준비 요청을 명시적으로 사용 설정해야 합니다. 이 경우 <warmup-requests-enabled> 값을 true로 설정한 다음 다시 배포해야 합니다.

<load-on-startup> 서블릿 사용

준비 로직을 제공하는 가장 쉬운 방법은 web.xml에서 자체 서블릿을 <load-on-startup>으로 표시하는 것입니다. 이 방법에서는 애플리케이션 코드를 변경할 필요가 없고, 애플리케이션이 초기화되면 지정된 모든 서블릿이 초기화됩니다.

web.xml 파일에서 시작 시 로드할 서블릿에 대해 <load-on-startup>1</load-on-startup> 요소를 <servlet> 요소에 추가합니다. 예를 들면 다음과 같습니다.

<servlet>
  <servlet-name>my-servlet</servlet-name>
  <servlet-class>com.company.MyServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

위 행은 지정된 서블릿 클래스를 로드하고 서블릿의 init() 메서드를 호출합니다. 준비 요청은 실시간 요청을 처리하기 전에 지정된 서블릿을 초기화합니다. 하지만 준비 요청이 없으면 <load-on-startup>에서 지정된 서블릿이 새 인스턴스에 대한 첫 번째 요청 시 등록되어 요청이 로드됩니다. 앞서 언급한 바와 같이 애플리케이션에 새 인스턴스가 필요할 때마다 App Engine은 준비 요청을 발행하지 않을 수 있습니다.

ServletContextListener 사용

서블릿이 호출되기 전에 실행할 커스텀 로직이 있는 경우 다음 안내를 따르세요.

  1. web.xml 파일에 ServletContextListener를 등록합니다.

    <listener>
      <listener-class>com.company.MyListener</listener-class>
    </listener>
    
  2. 서블릿 및 필터 코드와 함께 클래스를 제공합니다.

    public class MyListener implements ServletContextListener {
      public void contextInitialized(ServletContextEvent event) {
        // This will be invoked as part of a warmup request, or
        // the first user request if no warmup request was invoked.
      }
      public void contextDestroyed(ServletContextEvent event) {
        // App Engine does not currently invoke this method.
      }
    }
    

ServletContextListener는 준비 요청 중에 실행됩니다. 준비 요청이 없으면 새 인스턴스에 대한 첫 번째 요청 시에 실행됩니다. 이로 인해 요청이 로드될 수 있습니다.

커스텀 준비 서블릿 사용

커스텀 준비 서블릿은 준비 요청 중에 서블릿의 service 메서드를 호출합니다. 커스텀 준비 서블릿에 값비싼 로직을 배치하여 요청 로드 시 로드 시간 증가를 방지할 수 있습니다.

커스텀 준비 서블릿을 만들려면 web.xml에서 _ah_warmup에 대한 기본 제공 서블릿 정의를 재정의하면 됩니다.

<servlet>
  <servlet-name>_ah_warmup</servlet-name>
  <servlet-class>com.company.MyWarmupServlet</servlet-class>
</servlet>