자바 7 런타임 환경

경고: 자바 7 런타임은 지원 중단되었습니다. Google은 서비스 약관에 따라 자바 7 런타임을 계속 지원할 예정입니다. 가능한 조속히 자바 8 런타임으로 이전하세요.

App Engine을 사용하면 Google의 확장 가능한 인프라와 서비스를 사용하는 웹 애플리케이션을 빌드할 수 있습니다.

소개

App Engine은 안전한 '샌드박스' 환경에서 자바 7 JVM을 사용하여 자바 웹 애플리케이션을 실행합니다. App Engine은 앱의 서블릿 클래스를 호출하여 이 환경에서 요청을 처리하고 응답을 준비합니다.

OpenJDK 7을 기반으로 하는 App Engine 자바 7 런타임은 웹 애플리케이션용 자바 서블릿 2.5 표준을 사용합니다. 표준 WAR 디렉토리 구조에서 앱의 서블릿 클래스, JSP(JavaServer Page), 정적 파일, 데이터 파일을 배포 설명자(web.xml 파일)와 기타 구성 파일과 함께 제공합니다. App Engine은 배포 설명자에 따라 서블릿을 호출하여 요청을 처리합니다.

안전한 '샌드박스' 환경은 서비스와 보안을 위해 애플리케이션을 격리합니다. 다른 앱의 성능과 확장성을 방해하지 않는 작업만 앱이 수행할 수 있도록 합니다. 예를 들어, 앱은 특정한 방법으로 스레드를 생성하거나 로컬 파일 시스템에 데이터를 기록하거나 네트워크에 임의로 연결될 수 없습니다. 또한 앱은 JNI 또는 기타 네이티브 코드를 사용할 수 없습니다. JVM은 샌드박스 제한 내에서 작동하는 자바 바이트코드를 실행할 수 있습니다.

App Engine 플랫폼은 코드를 호출할 수 있는 다양한 서비스를 제공합니다. 애플리케이션은 지정된 간격으로 실행되는 예약된 작업을 구성할 수도 있습니다.

자바 방명록 앱 가이드는 자바 기술과 App Engine으로 웹 애플리케이션을 개발하는 방법을 소개합니다.

자바 런타임 선택

자바용 App Engine API는 App Engine SDK에 포함된 appengine-api-*.jar로 나타냅니다(여기서 *는 API와 App Engine SDK의 버전을 나타냄). 이 JAR을 애플리케이션의 WEB-INF/lib/ 디렉토리에 포함시켜 애플리케이션이 사용하는 API 버전을 선택하거나 Maven을 사용하여 종속 항목을 처리합니다. 기존 앱과 호환되지 않는 변경을 포함한 새 버전의 자바 런타임 환경이 출시될 경우, 해당 환경에는 새로운 버전 번호가 부여됩니다. 사용자가 JAR을 새 버전으로 교체할 때까지(최신 App Engine SDK) 애플리케이션은 계속 이전 버전을 사용합니다.

Maven을 사용하여 종속 항목 처리

Maven을 사용하여 모든 종속 항목을 관리할 수 있습니다. 예를 들어, 이 pom.xml 항목에는 Maven Central에서 제공되는 최신 App Engine API(Rappengine-api-1.0-sdk)가 포함되어 있습니다.

<dependency>
    <groupId>com.google.appengine</groupId>
    <artifactId>appengine-api-1.0-sdk</artifactId>
    <version>1.9.53</version>
</dependency>

샌드박스

App Engine이 애플리케이션에 대한 요청을 여러 웹 서버로 분산하도록 허용하고 애플리케이션 간에 간섭이 발생하지 않도록 하기 위해 애플리케이션은 제한된 '샌드박스' 환경에서 실행됩니다. 이 환경에서 애플리케이션은 코드를 실행하고, Cloud Datastore의 데이터를 쿼리하고, App Engine 메일, URL 가져오기, 사용자 서비스를 사용하고, 사용자의 웹 요청을 검사하여 응답을 준비할 수 있습니다.

App Engine 애플리케이션은 다음을 수행할 수 없습니다.

  • 파일 시스템에 쓰기. 애플리케이션은 Cloud Datastore를 사용하여 영구 데이터를 저장해야 합니다. 파일 시스템에서 읽는 작업은 허용되며 애플리케이션과 함께 업로드된 모든 애플리케이션 파일을 사용할 수 있습니다.

  • 응답 지연. 애플리케이션에 대한 웹 요청은 몇 초 이내에 처리되어야 합니다. 응답에 매우 오랜 시간이 걸리는 프로세스는 웹 서버의 과부하를 방지하기 위해 종료됩니다.

  • 기타 유형의 시스템 호출

파일 시스템

자바 애플리케이션은 파일 시스템에 쓰는 데 사용되는 어떠한 클래스도 사용할 수 없습니다(예: java.io.FileWriter). 애플리케이션은 java.io.FileReader와 같은 클래스를 사용하여 파일 시스템에서 고유 파일을 읽을 수 있습니다. 또한 애플리케이션은 Class.getResource() 또는 ServletContext.getResource()와 같은 '리소스'로 고유 파일에 액세스할 수 있습니다.

애플리케이션은 '리소스 파일'로 간주되는 파일만 파일 시스템을 통해 액세스할 수 있습니다. 기본적으로, WAR에 있는 모든 파일은 '리소스 파일'입니다. appengine-web.xml 파일을 사용하여 이 설정에서 파일을 제외할 수 있습니다.

java.lang.System

App Engine에 적용되지 않는 java.lang.System 클래스 기능은 중지됩니다.

exit(), gc(), runFinalization(), runFinalizersOnExit()와 같은 System 메소드는 App Engine에서 아무 작업도 수행하지 않습니다.

inheritedChannel(), console()과 같은 System 메소드는 null을 반환합니다.

앱은 어떠한 기본 JNI 코드도 제공하거나 직접 호출할 수 없습니다. 다음 System 메소드는 java.lang.SecurityException를 발생시킵니다. load(), loadLibrary(), setSecurityManager()

복습 자료

애플리케이션에는 자체 클래스에 대한 제한없는 완전한 리플렉션 액세스가 허용됩니다.

모든 비공개 구성원이 쿼리하고 java.lang.reflect.AccessibleObject.setAccessible() 메소드를 호출하며 비공개 구성원을 읽고 설정할 수 있습니다.

또한 애플리케이션은 java.lang.Stringjavax.servlet.http.HttpServletRequest와 같은 JRE 및 API 클래스에서 리플렉션할 수 있습니다. 하지만 보호되거나 비공개가 아닌 이러한 클래스의 공개 구성원에만 액세스할 수 있습니다.

애플리케이션은 자체에 속하지 않은 다른 어떤 클래스에 대해서도 리플렉션할 수 없고 setAccessible() 메소드를 사용하여 이러한 제한을 피할 수 없습니다.

커스텀 클래스 로드

커스텀 클래스 로드는 App Engine에서 완벽하게 지원됩니다. 애플리케이션은 애플리케이션별 클래스 로드 논리를 구현하는 ClassLoader의 고유 하위 클래스를 정의할 수 있습니다. 하지만 App Engine은 모든 ClassLoader를 재정의하여 애플리케이션에서 로드된 모든 클래스에 동일한 권한을 할당한다는 점에 유의해야 합니다. 커스텀 클래스 로드를 수행하는 경우, 신뢰할 수 없는 제3자 코드 로드 시 주의해야 합니다.

클래스 로더 JAR 순서 지정

때로는 클래스 이름간의 충돌을 해결하기 위해 클래스에 대해 JAR 파일을 검색하는 순서를 다시 정의해야 할 수도 있습니다. 이러한 경우, appengine-web.xml 파일에서 <priority-specifier> 요소를 포함한 <class-loader-config> 요소를 추가하여 특정 JAR 파일에 로드 우선 순위를 부여할 수 있습니다. 예를 들면 다음과 같습니다.

<class-loader-config>
  <priority-specifier filename="mailapi.jar"/>
</class-loader-config>

이 명령어는 war/WEB-INF/classes/ 디렉토리에 있는 파일을 제외하고 'mailapi.jar'를 첫 번째 JAR 파일로 지정하여 클래스 검색합니다.

여러 JAR 파일에 우선 순위가 지정되면 원래 로드 순서(서로에 대해 상대적으로)가 사용됩니다. 즉, <priority-specifier> 요소 자체의 순서는 중요하지 않습니다.

JRE 허용 목록

자바 표준 라이브러리(자바 런타임 환경 또는 JRE)의 클래스 액세스는 App Engine JRE 허용 목록의 클래스로 제한됩니다.

서명된 JAR 파일 없음

App Engine의 사전 컴파일은 서명된 JAR 파일과 호환되지 않습니다. 애플리케이션이 사전 컴파일된 경우에는(기본값) 서명된 JAR 파일을 로드할 수 없습니다. 애플리케이션이 서명된 JAR을 로드하려 하면 App Engine은 런타임에서 다음과 같은 예외를 생성합니다.

java.lang.SecurityException: SHA1 digest error for com/example/SomeClass.class
    at com.google.appengine.runtime.Request.process-d36f818a24b8cf1d(Request.java)
    at sun.security.util.ManifestEntryVerifier.verify(ManifestEntryVerifier.java:210)
    at java.util.jar.JarVerifier.processEntry(JarVerifier.java:218)
    at java.util.jar.JarVerifier.update(JarVerifier.java:205)
    at java.util.jar.JarVerifier$VerifierStream.read(JarVerifier.java:428)
    at sun.misc.Resource.getBytes(Resource.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:273)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:266)

이 문제를 해결하는 방법에는 두 가지가 있습니다.

스레드

자바 애플리케이션은 새 스레드를 만들 수 있지만 이를 수행하는 방법에는 몇 가지 제한 사항이 있습니다. 이 스레드는 이를 생성하는 요청보다 '오래 지속'될 수 없습니다.

애플리케이션은 다음을 수행할 수 있습니다.

그러나 ThreadManager의 메소드 중 하나를 사용하여 스레드를 만들어야 합니다. new Thread()를 직접 호출하거나 기본 스레드 팩터리를 사용할 수 없습니다.

애플리케이션은 thread.interrupt()와 같이 현재 스레드에 대해 작업을 수행할 수 있습니다.

각 요청은 동시 요청 스레드 50개로 제한됩니다. 단일 요청에서 스레드를 50개 이상 만들려고 하면 자바 런타임이 java.lang.IllegalStateException을 발생시킵니다.

스레드 사용 시 ExecutorRunnable과 같은 높은 수준의 동시 실행 객체를 사용합니다. 이러한 객체는 인터럽트스케줄링 및 부기와 같은 미묘하지만 중요한 다수의 동시 실행 세부정보를 처리합니다.

백그라운드 스레드

수동 또는 기본 확장 인스턴스에서 실행되는 코드는 스레드 생성 요청을 오래 지속할 수 있는 백그라운드 스레드를 시작할 수 있습니다. 그러면 인스턴스가 임의의 주기적 작업 또는 예약된 작업을 수행하거나 요청이 사용자에게 반환된 후에도 백그라운드에서 계속 작업할 수 있습니다.

자바 동시 실행은 디버깅하기 어려운 문제를 발생시킬 수 있습니다. 스레드를 사용하지 않으려면 작업 대기열, 예약 작업, 게시/구독을 사용하는 것이 좋습니다.

백그라운드 스레드의 로깅 항목은 생성 스레드의 항목과 독립적입니다. App Engine의 ThreadManager 문서에서 백그라운드 스레드를 자세하게 확인할 수 있습니다.

import com.google.appengine.api.ThreadManager;
import java.util.concurrent.atomic.AtomicLong;

AtomicLong counter = new AtomicLong();

Thread thread = ThreadManager.createBackgroundThread(new Runnable() {
  public void run() {
    try {
      while (true) {
        counter.incrementAndGet();
        Thread.sleep(10);
      }
    } catch (InterruptedException ex) {
      throw new RuntimeException("Interrupted in loop:", ex);
    }
  }
});
thread.start();

자동 확장 서비스에서 실행되는 코드가 백그라운드 스레드를 시작하려 하면 예외가 발생합니다.

App Engine API에서 생성되는 동시 백그라운드 스레드 최대 개수는 인스턴스당 10개입니다. (이 한도는 App Engine API와 관련되지 않은 일반 자바 스레드에 적용되지 않습니다.)

도구

지원되는 IDE

Cloud Tools for Eclipse는 App Engine 프로젝트용 Eclipse IDE에 새 프로젝트 마법사와 디버그 구성을 추가합니다. Eclipse 내에서 App Engine 프로젝트를 실시간으로 프로덕션에 배포할 수 있습니다.

Cloud Tools for IntelliJ를 사용하면 IntelliJ IDEA 내에서 App Engine 애플리케이션을 실행하고 디버깅할 수 있습니다. 또한, IDE를 벗어나지 않고 App Engine 프로젝트를 실시간으로 프로덕션에 배포할 수 있습니다.

지원되는 빌드 도구

Apache Maven 또는 Gradle용 App Engine 플러그인을 사용하여 개발 프로세스 속도를 높일 수 있습니다.

로컬 개발 서버

개발 서버는 개발과 테스트를 위해 로컬 컴퓨터에서 애플리케이션을 실행합니다. 서버는 Cloud Datastore 서비스를 시뮬레이션합니다. 개발 서버는 테스트 중에 앱이 수행하는 쿼리를 기반으로 Cloud Datastore 색인에 대한 구성을 생성할 수도 있습니다.

AppCfg

AppCfg는 자바용 App Engine SDK에 포함되어 있습니다. 이 도구는 App Engine에서 실행되는 애플리케이션과의 명령줄 상호작용을 처리하는 다용도 도구입니다. AppCfg는 App Engine에 애플리케이션을 업로드하거나 Cloud Datastore 색인 구성을 업데이트하므로, 코드를 업데이트하기 전에 새 색인을 빌드할 수 있습니다. 또한 앱의 로그 데이터를 다운로드할 수도 있으므로, 자체 도구를 사용하여 앱 성능을 분석할 수 있습니다.

동시 실행 및 지연 시간

애플리케이션 지연 시간은 트래픽을 처리하는 데 필요한 인스턴스 수에 가장 큰 영향을 줍니다. 요청을 신속하게 처리하면 단일 인스턴스는 많은 요청을 처리할 수 있습니다.

단일 스레드 인스턴스는 동시 요청 한 개를 처리할 수 있으므로, 지연 시간과 인스턴스에서 처리 가능한 초당 요청 수 사이에는 직접적인 관계가 있습니다. 예를 들어, 10ms 지연 시간은 100 요청/초/인스턴스에 해당됩니다.

다중 스레드 인스턴스는 다수의 동시 요청을 처리할 수 있으므로, 소비된 CPU와 초당 요청 수 사이에는 직접적인 관계가 있습니다.

자바 앱은 동시 요청을 지원하므로, 다른 요청이 완료되기를 기다리는 동안 단일 인스턴스가 새 요청을 처리할 수 있습니다. 동시 실행은 앱에 필요한 인스턴스 수를 상당히 줄이지만, 앱이 멀티 스레딩을 사용하도록 설계해야 합니다.

예를 들어, B4 인스턴스(약 2.4GHz)가 요청당 10Mcycle을 소비하는 경우 초 및 인스턴스당 요청 240개를 처리할 수 있습니다. 인스턴스가 요청당 100Mcycle을 소비하는 경우, 초 및 인스턴스당 요청 24개를 처리할 수 있습니다. 이 숫자는 이상적인 경우이지만 인스턴스 한 개에서 수행할 수 있는 작업 관점에서는 상당히 현실적인 숫자입니다.

이 페이지가 도움이 되었나요? 평가를 부탁드립니다.

다음에 대한 의견 보내기...

App Engine standard environment for Java