My favorites | 中文(繁體) | Sign in
英文版或許有比此中譯版新的內容

Java Servlet 環境

「應用服務引擎」會在安全的「沙箱」環境中,使用 Java 6 JVM 執行您的 Java 網路應用程式。「應用服務引擎」會叫用應用程式的 servlet 類別,在這個環境中處理要求與準備回應。

選取 Java API 版本

在您使用 Java SDK 的 AppCfg 工具上傳應用程式時,「應用服務引擎」會為您的應用程式提供 Java 執行階段環境。

截至筆者撰稿時,「應用服務引擎 Java API」僅提供一個版本。這個 API 是由 SDK 的 appengine-api-*.jar 代表之 (其中 * 代表 API 和 SDK 的版本)。您可以將 JAR 納入 WEB-INF/lib/ 目錄,藉此選取應用程式要使用的 API 版本。如果 Java 執行階段環境推出新的版本,卻新增了無法與現有應用程式相容的變更,則該環境將擁有新的版本號碼。您的應用程式將繼續使用之前的版本,直到您使用新的版本 (來自較新的 SDK) 取代 JAR,並重新上傳應用程式為止。

要求和 Servlet

收到應用程式的網頁要求時,「應用服務引擎」會叫用與 URL 相應的 servlet,如應用程式部署描述元 (WEB-INF/ 目錄的 web.xml 檔案) 所述。它會使用 Java Servlet API,為 servlet 提供要求資料,以及接收回應資料。

「應用服務引擎」使用多個網頁伺服器來執行您的應用程式,並可靠地自動調整用來處理要求的伺服器數量。指定的要求可能會透過傳送到任一部伺服器,而且處理它的伺服器,與來自相同使用者之前送出要求的處理伺服器,可能不是同一部。

下列 servlet 類別範例會在使用者的瀏覽器中顯示簡單的訊息。

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/plain");
        resp.getWriter().println("Hello, world");
    }
}

回應

「應用服務引擎」會呼叫擁有要求物件和回應物件的 servlet,然後等待 servlet 填入回應物件並傳回。Servlet 傳回時,回應物件上的資料會傳送給使用者。

「應用服務引擎」不支援先傳送資料給用戶端,然後在應用程式中執行更多計算,再傳送更多資料。換言之,「應用服務引擎」不支援在回應單一要求時「串流」資料。

如果用戶端傳送 HTTP 標頭的要求指出用戶端可接受壓縮的 (gzip'd) 內容,則「應用服務引擎」會自動壓縮回應資料,並附加適當的回應標頭。它會使用 Accept-EncodingUser-Agent 的要求標頭來判斷用戶端是否能夠可靠地接收壓縮的回應。自訂用戶端可以強制壓縮內容,只要將 Accept-EncodingUser-Agent 標頭指定為「gzip」值即可。

要求計時器

要求處理常式必須在限制時間內 (一般是在 30 秒左右),根據要求產生並傳回回應。一旦超過限制時間,要求處理常式將遭到中斷。

Java 執行階段環境會擲回 com.google.apphosting.api.DeadlineExceededException,以中斷 servlet。如果要求處理常式未能捕捉此例外狀況,則與所有未捕捉到的例外狀況一樣,執行階段環境將傳回 HTTP 500 伺服器錯誤給用戶端。

要求處理常式可以捕捉此錯誤,並自訂回應。執行階段環境會在引發例外狀況後,給予要求處理常式較多時間 (一秒以內) 準備自訂回應。

回應一個要求的時間可能長達 30 秒,不過如果應用程式的要求都是短期要求,尤其是只需花費幾百毫秒的要求,則「應用服務引擎」將能展現最佳效能。有效率的應用程式可以快速回應絕大多數的要求。無法快速回應的應用程式,將無法順利地透過「應用服務引擎」的基礎結構擴充。

沙箱

為了讓「應用服務引擎」將應用程式的要求分散到多個網頁伺服器,並防止應用程式彼此干擾,應用程式會在限制的「沙箱」環境中執行。在此環境中,應用程式可以執行程式碼、儲存和查詢「應用服務引擎」資料存放區的資料、使用「應用服務引擎」郵件、URL 擷取和使用者服務以及檢查使用者的網路要求和準備回應。

「應用服務引擎」無法執行下列動作:

  • 寫入檔案系統。應用程式必須使用「應用服務引擎」資料存放區來儲存持續性的資料。「應用服務引擎」可以讀取檔案系統,也可以使用所有與應用程式一起上傳的應用程式檔案。
  • 開啟通訊端或直接存取其他主機。應用程式可以使用「應用服務引擎」URL 擷取服務,讓 HTTP 和 HTTPS 要求分別轉向其他主機的連接埠 80 和 443。
  • 產生子處理程序或執行緒。應用程式的網頁要求必須在單一處理程序中的幾秒內進行處理。如果處理程序回應時間過長,便會終止,以避免網頁伺服器負載過重。
  • 發出其他種類的系統呼叫。

執行緒

Java 應用程式無法建立新的 java.lang.ThreadGroup 或新的 java.lang.Thread。這些限制也可以套用至使用執行緒的 JRE 類別。例如,應用程式無法建立新的 java.util.concurrent.ThreadPoolExecutorjava.util.Timer。應用程式「可以」對目前執行緒執行作業,例如 Thread.currentThread().dumpStack()

檔案系統

Java 應用程式無法使用任何用於寫入檔案系統的類別,例如 java.io.FileWriter。應用程式可以使用諸如 java.io.FileReader 的類別,從檔案系統讀取自己的檔案。應用程式也可以將自己的檔案當做「資源」存取,例如使用 Class.getResource()ServletContext.getResource()

只有被歸類為「資源檔案」的檔案才能提供應用程式透過檔案系統存取。按照預設,WAR 的所有檔案均為「資源檔案」。您可以使用 appengine-web.xml 檔案排除 WAR 的檔案。

java.lang.System

所有不適用於「應用服務引擎」的 java.lang.System 類別功能會遭到停用。

下列的 System 方法在「應用服務引擎」中沒有作用:exit()gc()runFinalization()runFinalizersOnExit()

下列的 System 方法會傳回 nullinheritedChannel()console()

應用程式無法提供或直接叫用任何原生的 JNI 程式碼。下列的 System 方法會引發 java.lang.SecurityExceptionload()loadLibrary()setSecurityManager()

反映

應用程式可以完全、無限制地反映存取自己的檔案。它可以查詢任何私有成員 (使用 java.lang.reflect.AccessibleObject.setAccessible()),以及讀取或設定私有成員。

應用程式也可以反映在 JRE 和 API 類別上,例如 java.lang.Stringjavax.servlet.http.HttpServletRequest。不過,它只能存取這些類別的公開成員,無法存取受保護或私有成員。

應用程式無法反映任何不屬於它的類別,也無法透過 setAccessible() 方法規避這些限制。

自訂類別載入

「應用服務引擎」完全支援自訂類別載入。 不過請注意,「應用服務引擎」會覆寫所有 ClassLoader,以指派相同權限給應用程式所載入的所有類別。如果您要執行自訂類別載入,載入不信任的第三方程式碼時請特別留意。

JRE 白名單

存取 Java 標準程式庫 (Java 執行階段環境,或 JRE) 的類別時,僅限存取「應用服務引擎 JRE 白名單」所列出的類別。

記錄

您的應用程式可以使用 java.util.logging.Logger,將資訊寫入應用程式記錄。應用程式的記錄資料可以透過「管理控制台」檢視與分析,也可以透過 appcfg.sh request_logs 下載。「管理控制台」可以辨識 Logger 類別的記錄等級,並透過互動方式顯示不同等級的訊息。

「應用服務引擎」會擷取 servlet 寫入標準輸出串流 (System.out) 和標準錯誤串流 (System.err) 的所有資料,並記錄在應用程式記錄中。寫入標準輸出串流的資料會列為「INFO」等級記錄,而寫入標準錯誤串流的資料則會列為「WARNING」等級記錄。所有能夠記錄輸出或錯誤串流的記錄架構 (例如 log4j) 均可運作。不過,如需更精確地控制「管理控制台」的記錄等級,記錄架構必須使用 java.util.logging 介面卡。

import java.util.logging.Logger;
// ...

public class MyServlet extends HttpServlet {
    private static final Logger log = Logger.getLogger(MyServlet.class.getName());

    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        log.info("An informational message.");

        log.warning("A warning message.");

        log.severe("An error message.");
    }
}

「應用服務引擎 Java SDK」的 appengine-java-sdk/config/user/目錄包括 logging.properties 範本檔。如要使用這個範本,請將檔案複製到您的 WEB-INF/classes 目錄 (或 WAR 中的其他位置),然後將系統屬性 java.util.logging.config.file 複製到 "WEB-INF/classes/logging.properties" (或應用程式根目錄下的自選路徑)。您可以在 appengine-web.xml 檔案中設定系統屬性,如下所示:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    ...

    <system-properties>
        <property name="java.util.logging.config.file" value="WEB-INF/classes/logging.properties" />
    </system-properties>

</appengine-web-app>

「Eclipse 專用的 Google 外掛程式」新建專案精靈會建立這些記錄設定檔,並自動將它們複製到 WEB-INF/classes/。如果是 java.util.logging,您必須設定系統屬性才能使用這個檔案。

環境

所有的系統屬性和環境變數都專屬於您的應用程式。設定系統屬性只會影響應用程式的屬性檢視畫面,不會影響 JVM 的檢視畫面。

您可以在部署描述元中設定應用程式的系統屬性和環境變數。

當「應用服務引擎」在應用程式伺服器初始化 JVM 時,它會設定下列系統屬性:

  • file.separator
  • path.separator
  • line.separator
  • java.version
  • java.vendor
  • java.vendor.url
  • java.class.version
  • java.specification.version
  • java.specification.vendor
  • java.specification.name
  • java.vm.vendor
  • java.vm.name
  • java.vm.specification.version
  • java.vm.specification.vendor
  • java.vm.specification.name
  • user.dir

配額和限制

應用程式的每個連入要求會算入「要求」的配額中。

接收要求而收到的資料會算入「連入頻寬 (可計費)」的配額中。回應要求而傳送的資料會算入「連出頻寬 (可計費)」的配額中。

HTTP 和 HTTPS (安全) 要求會算入「要求」、「連入頻寬 (可計費)」以及「連出頻寬 (可計費)」的配額中。「管理控制台」的 [Quota Details] (配額詳細資訊) 頁面也會另外提供「安全要求」、「安全連入頻寬」以及「安全連出頻寬」的數據,以供參考。只有 HTTPS 要求才會算入這些數據。

執行要求處理常式所花費的 CPU 處理時間會算入「CPU 時間 (可計費)」的配額中。

如需瞭解配額的詳細資訊,請參閱「配額」,或參閱「管理控制台」的「配額詳細資訊」一節。

除了配額之外,使用要求處理常式時也需遵循下列限制:

限制 大小
要求大小 10 MB
回應大小 10 MB
要求持續時間 30 秒
同時動態要求 30 *
應用程式檔案數量上限 1,000
靜態檔案數量上限 1,000
應用程式檔案大小上限 10 MB
靜態檔案大小上限 10 MB
所有應用程式和靜態檔案的總計大小上限 150 MB

* 應用程式可以同時處理大約 30 個作用中的動態要求。這表示如果一個應用程式處理伺服器端要求的平均時間為 75 毫秒,則在沒有發生任何額外延遲的狀況下,每一秒最多可以處理 400 個要求 (1000 毫秒/秒 / 75 毫秒/要求 * 30 = 400)。為挪出空間給其他共用相同伺服器的應用程式,受到 CPU 嚴重限制的應用程式在處理長期執行的要求時,可能會發生額外的延遲。靜態檔案的要求則不會受到此限制的影響。