My favorites | 中文(简体) | Sign in

Java Servlet 环境

App Engine 在安全的“沙盒”环境中使用 Java 6 JVM 来运行 Java 网络应用程序。App Engine 将在此环境中调用应用程序的 servlet 类来处理请求并准备响应。

选择 Java API 版本

在您使用 Java SDK 中的 AppCfg 工具来上传应用程序时,App Engine 知道为该应用程序使用 Java 运行时环境。

就本文而言,App Engine Java API 只有一个版本。此 API 由附带 SDK 的 appengine-api-*.jar 表示(其中 * 表示 API 和 SDK 的版本)。通过将此 JAR 加入到应用程序的 WEB-INF/lib/ 目录,选择应用程序使用的 API 的版本。如果发布的新版本 Java 运行时环境引入了与现有应用程序不兼容的更改,则该环境将拥有一个新的版本号。在用新的版本(来自于最新的 SDK)替换 JAR 并重新上传应用程序之前,您的应用程序将继续使用先前的版本。

请求和 Servlet

当 App Engine 收到应用程序的网络请求时,将调用与网址相对应的 servlet,如应用程序的部署描述符WEB-INF/ 目录中的 web.xml 文件)中所述。它使用 Java Servlet API 向 servlet 提供请求数据,并接受响应数据。

App Engine 使用多个网络服务器运行您的应用程序,并自动调整它所使用的服务器数量以便可靠地处理请求。指定的请求可能会传送到任何服务器,而且可能不是处理先前来自同一用户的请求的服务器。

以下示例 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");
    }
}

响应

App Engine 调用带有请求对象和响应对象的 servlet,然后等待其将填充响应对象并返回。servlet 返回时,响应对象上的数据将发送给用户。

App Engine 不支持向客户端发送数据,在应用程序中执行更多计算,然后发送更多数据。换句话说,App Engine 不支持响应单个请求“流式处理”数据。

如果客户端发送带有指明该客户端可接受压缩(通过 gzip)内容的请求的 HTTP 标头,则 App Engine 会自动压缩该响应数据并附加相应的响应标头。它同时使用 Accept-EncodingUser-Agent 请求标头来确定客户端是否可以可靠地接收压缩响应。自定义客户端可通过指定 Accept-EncodingUser-Agent 标头(带有“gzip”值)强行压缩内容。

请求计时器

请求处理程序对请求生成和返回响应的时间是有限的,通常约为 30 秒。达到限制时间后,请求处理程序将中断。

Java 运行时环境通过引发 com.google.apphosting.api.DeadlineExceededException 中断 servlet。如果请求处理程序不捕获此异常,那么和所有未捕获的异常一样,运行时环境将向客户端返回 HTTP 500 服务器错误。

请求处理程序可以捕获此错误来自定义响应。运行时环境在引发异常以便准备自定义响应之后,将为请求处理程序提供更多一点的时间(少于一秒)。

虽然请求有 30 秒的时间来响应,但 App Engine 针对应用程序进行过优化,请求时间很短,通常只需要几百毫秒。高效的应用程序对大多数的请求都能够很快作出响应。响应不快的应用程序将不能根据 App Engine 基础结构而进行灵活的调整。

沙盒

为了使得 App Engine 能够跨多个网络服务器分配对于应用程序的请求,并且防止应用程序彼此干扰,请在受限制的“沙盒”环境中运行应用程序。在这种环境中,该应用程序可执行代码;可存储和查询 App Engine 数据存储区中的数据;可使用 App Engine 邮件、网址抓取和用户服务;可检查用户的网络请求以及准备响应。

App Engine 应用程序无法:

  • 向文件系统写入。应用程序必须使用 App Engine 数据存储区存储永久数据。允许从文件系统中读取,并且可使用与该应用程序一起上传的所有应用程序文件。
  • 打开套接字或直接访问另一主机。应用程序可使用 App Engine 网址抓取服务分别向端口 80 和 443 上的其他主机发出 HTTP 和 HTTPS 请求。
  • 产生子进程或线程。必须在几秒钟内于单个进程中处理对应用程序的网络请求。响应时间很长的进程会被终止,以避免使网络服务器负载过重。
  • 进行其他类型的系统调用。

线程

Java 应用程序无法新建 java.lang.ThreadGroupjava.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 文件将文件从该组中排除出去。

java.lang.System

禁用不适用于 App Engine 的 java.lang.System 类的功能。

以下 System 方法在 App Engine 中不起作用: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() 方法来避开这些限制。

自定义类载入

App Engine 完全支持自定义类载入。但是请注意,App Engine 将覆盖所有的 ClassLoader,以将相同的权限分配给所有由应用程序载入的类。如果执行自定义类载入,在载入不信任的第三方代码时要小心。

JRE 白名单

对 Java 标准库(Java 运行时环境或 JRE)中类的访问仅限于 App Engine JRE 白名单中的类。

日志

应用程序可以使用 java.util.logging.Logger 将信息写入应用程序日志。您可以使用管理控制台查看和分析您的应用程序的日志数据,或使用 appcfg.sh request_logs 下载日志数据。管理控制台可以识别 Logger 类的日志级别,并且以交互方式显示不同级别的消息。

servlet 写入标准输出流 (System.out) 和标准错误流 (System.err) 中的所有内容都由 App Engine 捕捉,并记录在应用程序日志中。写入标准输出流的行将以“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.");
    }
}

App Engine 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 的视图。

您可以在部署描述符中为应用程序设置系统属性和环境变量。

App Engine 在应用程序服务器上初始化 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(安全)请求都会计算在请求传入带宽(计费)传出带宽(计费)配额内。管理控制台的“配额详细信息”页也出于信息方面的考虑,将安全请求安全传入带宽安全传出带宽报告为单独的值。只有 HTTPS 请求计算在这些值内。

执行请求处理程序所花费的 CPU 处理时间会计算在 CPU 时间(计费)配额内。

有关配额的详细信息,请参阅配额,以及管理控制台的“配额详细信息”部分。

除了配额以外,请求处理程序还遵循以下限制i:

限制
请求大小 10 兆字节
响应大小 10 兆字节
请求持续时间 30 秒
同时动态请求 30 *
应用程序文件的最大数目 1,000
静态文件的最大数目 1,000
应用程序文件的最大大小 10 兆字节
静态文件的最大大小 10 兆字节
所有应用程序和静态文件的最大总大小 150 兆字节

* 应用程序可以同时处理约 30 个活动的动态请求。这表示平均服务器端请求处理时间为 75 毫秒的应用程序可以达到最高为(1000 毫秒/秒 / 75 毫秒/请求)* 30 = 400 请求/秒的效率,且没有任何额外滞后时间。很大程度上受 CPU 制约的应用程序在长时间请求中可能会出现一些额外滞后时间,以便为分享相同服务器的其他应用程序腾出空间。对于静态文件的请求不受此限制的影响。