65.9K
CodeProject 正在变化。 阅读更多。
Home

Google App Engine - Go 教程

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2014 年 12 月 1 日

CC (Attr 3U)

19分钟阅读

viewsIcon

12227

在本教程结束时,您将实现一个可工作的应用程序,一个简单的留言簿,允许用户将消息发布到公共留言板。

引言

欢迎使用 Google App Engine!创建 App Engine 应用程序很容易,只需几分钟。而且它免费开始:立即上传您的应用程序并与用户分享,无需付费,无需承诺。

Google App Engine 应用程序可以用 Go、Java、Python 或 PHP 编程语言编写。本教程将介绍 Go。如果您更倾向于使用 Java、Python 或 PHP 来构建您的应用程序,请参阅 JavaPython 2.7PHP 指南。

在本教程中,您将学习如何

  • 构建一个 App Engine Go 应用程序,
  • 使用 Go 的 http 包 来提供网页,
  • 使用 Go 的 datastore API 操作 App Engine 数据存储,
  • 将 App Engine 应用程序与 Google Accounts 集成以进行用户身份验证,
  • 将 Go 的 html/template 包 与您的应用程序一起使用,以及
  • 将您的应用程序上传到 App Engine。

在本教程结束时,您将实现一个可工作的应用程序,一个简单的留言簿,允许用户将消息发布到公共留言板。

要开始开发 Google App Engine 应用程序,您需要下载并设置 App Engine 软件开发工具包。

开发环境

您使用 App Engine Go 软件开发工具包 (SDK) 为 Google App Engine 开发和上传 Go 应用程序。

Go SDK 包含一个 Web 服务器应用程序,它可以模拟 App Engine 环境,包括本地版本的数据存储、Google Accounts,以及通过 App Engine API 直接从您的计算机获取 URL 和发送电子邮件的功能。Go SDK 使用 Python SDK 中略有修改的开发工具版本,并且可以在任何配备 Python 2.7 的 Intel 架构的 Mac OS X、Linux 或 Windows 计算机上运行。

如果需要,请从 Python 网站 下载并安装适用于您平台的 Python 2.7。大多数 Mac OS X 用户已经安装了 Python 2.7。如果您在使用 Python 工具时遇到问题,请确保您已安装 Python 2.7。

下载并安装适用于您操作系统的 App Engine Go SDK

在本教程中,您将使用 SDK 中的 goapp 工具的两个命令

您可以在 zip 存档的 go_appengine 目录中找到这些命令。

为了简化开发和部署,请考虑将此目录添加到您的 PATH 环境变量中。这可以通过将以下行添加到您的 $HOME/.profile$HOME/.bashrc 或等效文件中来完成

export PATH=/path/to/go_appengine:$PATH

本地开发环境允许您在将完整的 App Engine 应用程序展示给全世界之前进行开发和测试。让我们编写一些代码。

Hello, World!

Go App Engine 应用程序通过与 Go 的 http 包兼容的 Web 服务器与外部世界通信。这使得编写 Go App Engine 应用程序与编写独立的 Go Web 应用程序非常相似。

让我们从实现一个显示短消息的小应用程序开始。

创建一个简单的 HTTP 处理程序

创建一个名为 myapp 的目录。此应用程序的所有文件都将保存在此目录中。

myapp 目录内,创建一个名为 hello.go 的文件,并为其添加以下内容

package hello

import (
    "fmt"
    "net/http"
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, world!")
}

这个 Go 包响应任何请求,发送一个包含消息 Hello, world! 的响应。

注意: 在编写独立的 Go 程序时,我们会将此代码放在 package main 中。Go App Engine Runtime 提供了一个特殊的 main 包,因此您应该将 HTTP 处理程序代码放在您选择的包中(在本例中为 hello)。

创建配置文件

App Engine 应用程序有一个名为 app.yaml 的配置文件。除其他外,此文件会告诉 App Engine 服务使用哪个运行时以及哪些 URL 应该由我们的 Go 程序处理。

myapp 目录内,创建一个名为 app.yaml 的文件,并包含以下内容

application: helloworld
version: 1
runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app

从上到下,此配置文件描述了此应用程序的以下内容

  • 应用程序标识符是 helloworld。当您在本教程后面注册您的应用程序时,您将选择一个唯一的标识符,并更新此值。开发期间此值可以是任何内容。目前,请将其保留为 helloworld
  • 这是此应用程序代码的第 1 版。如果您在上传新版本应用程序软件之前调整此值,App Engine 将保留之前的版本,并允许您使用管理控制台回滚到之前的版本。
  • 此代码在 go 运行时环境中运行,API 版本为 go1
  • 路径匹配正则表达式 /.*(所有 URL)的任何请求都应由 Go 程序处理。_go_app 值是开发 Web 服务器识别的特殊字符串;生产 App Engine 服务器会忽略它。

注意: Go SDK 的工作方式与 Python 和 Java SDK 不同:给定应用程序的所有 Go 包都编译成单个可执行文件,并且请求分派由 Go 程序本身处理。这就是为什么我们在 init 函数中调用 http.HandleFunc 来将我们的 handler 与 Web 根目录("/")关联起来。但是,您仍然可以使用 app.yaml 文件来配置提供静态文件或需要特殊权限的路径。

此文件的语法是 YAML。有关配置选项的完整列表,请参阅 Go 应用程序配置页面。

测试应用程序

有了 hello 包和将每个 URL 映射到 Go 程序的配置文件,应用程序就完成了。您现在可以使用 App Engine SDK 随附的 Web 服务器对其进行测试。

检查您是否将所有内容都放在了正确的位置。应用程序的目录结构应如下所示

myapp/
  app.yaml
  hello.go

运行以下命令,并提供 myapp 目录的路径,以编译您的应用程序并启动开发 Web 服务器

/path/to/go_appengine/goapp serve myapp/

如果您已按建议将 /path/to/go_appengine/ 添加到您的 PATH,则可以省略它。如果 myapp 是您的当前目录,您也可以完全省略路径,因此命令将是

goapp serve

Web 服务器现在正在运行,侦听端口 8080 上的请求。通过在 Web 浏览器中访问以下 URL 来测试应用程序

有关运行开发 Web 服务器的更多信息,包括如何更改它使用的端口,请参阅 开发服务器参考,或运行 goapp help serve

迭代开发

开发应用程序服务器会检测到文件中的更改。当您更新源代码时,它会重新编译它们并重新启动您的本地应用程序。无需重新启动 goapp

现在就试试:保持 Web 服务器运行,然后编辑 hello.goHello, world! 更改为其他内容。重新加载 https://:8080/ 以查看更改。

要关闭 Web 服务器,请确保终端窗口处于活动状态,然后按 Control-C(或您控制台的相应“中断”键)。

在本教程的其余部分,请保持 Web 服务器运行。如果您需要停止它,可以通过再次运行上述命令来重新启动它。

现在您拥有了一个完整的 App Engine 应用程序!您可以立即部署这个简单的问候语并与全球用户共享。但在我们部署它之前,让我们添加一些功能。

使用用户服务

Google App Engine 提供基于 Google 基础架构的几个有用服务。其中一个例子是 Users 服务,它允许您的应用程序与 Google 用户帐户集成。通过 Users 服务,您的用户可以使用他们已有的 Google 帐户登录您的应用程序。

Users 服务可以轻松地个性化此应用程序的问候。

使用用户

再次编辑 myapp/hello.go,并用以下内容替换其内容

package hello

import (
    "fmt"
    "net/http"

    "appengine"
    "appengine/user"
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    u := user.Current(c)
    if u == nil {
        url, err := user.LoginURL(c, r.URL.String())
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Set("Location", url)
        w.WriteHeader(http.StatusFound)
        return
    }
    fmt.Fprintf(w, "Hello, %v!", u)
}

重新加载浏览器中的页面。您的应用程序会显示一个链接,点击该链接将重定向到适合测试您应用程序的本地版 Google 登录页面。您可以在此屏幕上输入任何您想要的用户名,您的应用程序将看到一个基于该用户名的假 user.User 值。

当您的应用程序在 App Engine 上运行时,用户将被定向到 Google 帐户登录页面,然后在成功登录或创建帐户后重定向回您的应用程序。

用户 API

让我们仔细看看新部分

// Create a new context.
c := appengine.NewContext(r)

appengine.NewContext 函数返回一个与当前请求关联的 appengine.Context 值。这是一个不透明的值,Go App Engine SDK 中的许多函数都使用它来与 App Engine 服务通信。

// Get the current user.
u := user.Current(c)

如果用户已登录您的应用程序,user.Current 将返回指向该用户 user.User 值的指针。否则,它将返回 nil

if u == nil {
    url, err := user.LoginURL(c, r.URL.String())
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    w.Header().Set("Location", url)
    w.WriteHeader(http.StatusFound)
    return
}

如果用户未登录,请通过设置 Location 标头并返回 302“Found”HTTP 状态码,将用户的浏览器重定向到 Google 帐户登录屏幕。重定向包含此页面的 URL(r.URL.String()),以便 Google 帐户登录机制在用户登录或注册新帐户后将用户送回此处。

user.LoginURL 函数将其第二个参数作为 error 值返回。虽然这里不太可能发生错误,但检查它并在适当的时候(在本例中,使用 http.Error 助手)向用户显示错误是一个好习惯。

// Display an output message with user name.
fmt.Fprintf(w, "Hello, %v!", u)

如果用户已登录,请使用与用户帐户关联的名称显示个性化消息。在这种情况下,fmt.Fprintf 函数调用 *user.UserString 方法以获取用户名称的字符串形式。

有关 Users API 的更多信息,请参阅 Users 参考

注意: 对于仅应提供给已登录用户访问的特定路径,请在 app.yaml 文件中使用 login: required 指令。有关详细信息,请参阅 app.yaml 参考

我们的应用程序现在可以根据姓名问候访问用户了。让我们添加一个允许用户互相问候的功能。

处理表单

如果我们希望用户能够发布他们自己的问候语,我们就需要一种方法来处理用户通过 Web 表单提交的信息。Go 的 http 包可以轻松处理表单数据。

处理 Web 表单

myapp/hello.go 的内容替换为以下内容

package hello

import (
    "fmt"
    "html/template"
    "net/http"
)

func init() {
    http.HandleFunc("/", root)
    http.HandleFunc("/sign", sign)
}

func root(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, guestbookForm)
}

const guestbookForm = `
<html>
  <body>
    <form action="/sign" method="post">
      <div><textarea name="content" rows="3" cols="60"></textarea></div>
      <div><input type="submit" value="Sign Guestbook"></div>
    </form>
  </body>
</html>
`

func sign(w http.ResponseWriter, r *http.Request) {
    err := signTemplate.Execute(w, r.FormValue("content"))
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

var signTemplate = template.Must(template.New("sign").Parse(signTemplateHTML))

const signTemplateHTML = `
<html>
  <body>
    <p>You wrote:</p>
    <pre>{{.}}</pre>
  </body>
</html>
`

重新加载页面以查看表单,然后尝试提交消息。

此版本有两个处理程序:路径 / 映射到 root,它显示一个 Web 表单。路径 /sign 映射到 sign,它显示 Web 表单提交的数据。

sign 函数通过调用 r.FormValue 获取表单数据,并将其传递给 signTemplate.Execute,后者将渲染的模板写入 http.ResponseWriter。在模板代码中,内容会自动过滤以转义 HTML 特殊字符。自动转义是 html/template 包的特性,与 text/template 不同。

现在我们能够收集用户的信息了,我们需要一个地方来存放它们,以及一种检索它们的方法。

使用数据存储

在可伸缩的 Web 应用程序中存储数据可能很棘手。用户可能随时与数十个 Web 服务器中的任何一个进行交互,并且用户的下一个请求可能与处理前一个请求的 Web 服务器不同。Web 服务器可能依赖于分散在数十台机器上的数据,这些机器可能位于世界各地的不同位置。

感谢 Google App Engine,您不必担心这些。App Engine 的基础架构通过简单的 API 处理所有数据分发、复制和负载均衡——而且您还可以获得一个强大的查询引擎。

App Engine 的数据存储库——高复制数据存储 (HRD)——使用 Paxos 算法 将数据复制到多个数据中心。数据以称为实体的对象形式写入数据存储。每个实体都有一个,它唯一地标识该实体。实体可以选择将另一个实体指定为其父级;第一个实体是父实体的子级。因此,数据存储中的实体形成一个层次结构空间,类似于文件系统的目录结构。实体的父级、父级的父级,依此类推递归地,是其祖先;其子级、子级的子级,依此类推,是其后代。没有父级的实体是根实体

在灾难性故障面前,数据存储具有极高的弹性,但其一致性保证可能与您熟悉的不同。源自共同祖先的实体被称为属于同一实体组;共同祖先的键是该组的父键,它用于标识整个组。对单个实体组进行的查询,称为祖先查询引用父键而不是特定实体的键。实体组是具有一致性和事务性的单位:尽管跨多个实体组的查询可能会返回陈旧的、最终一致的结果,但仅限于单个实体组的查询始终返回最新的、强一致的结果。

本指南中的代码示例将相关实体组织到实体组中,并对这些实体组使用祖先查询以返回强一致的结果。在示例代码注释中,我们重点介绍了一些这可能会影响您的应用程序设计的方式。有关更详细的信息,请参阅 为强一致性构造数据

存储提交的问候语

对于留言簿应用程序,我们希望存储用户发布的问候语。每个问候语都包含作者姓名、消息内容以及消息发布日期和时间,以便我们可以按时间顺序显示消息。

为了表示这些数据,我们创建了一个名为 Greeting 的 Go 结构

type Greeting struct {
        Author  string
        Content string
        Date    time.Time
}

现在我们有了一个问候语的数据类型,应用程序可以创建新的 Greeting 值并将它们放入数据存储中。sign 处理程序的这个新版本正是这样做的

func sign(w http.ResponseWriter, r *http.Request) {
        c := appengine.NewContext(r)
        g := Greeting{
                Content: r.FormValue("content"),
                Date:    time.Now(),
        }
        if u := user.Current(c); u != nil {
                g.Author = u.String()
        }
        // We set the same parent key on every Greeting entity to ensure each Greeting
        // is in the same entity group. Queries across the single entity group
        // will be consistent. However, the write rate to a single entity group
        // should be limited to ~1/second.
        key := datastore.NewIncompleteKey(c, "Greeting", guestbookKey(c))
        _, err := datastore.Put(c, key, &g)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
        http.Redirect(w, r, "/", http.StatusFound)
}

这会创建一个新的 Greeting 值,将其 Author 字段设置为当前用户,Content 字段设置为用户提交的数据,Date 字段设置为当前时间。

最后,datastore.Put 将我们新值保存到数据存储中。我们传递给它一个新的、不完整的键,以便数据存储会自动为该记录创建新的键。

由于高复制数据存储中的查询在实体组内部才是强一致的,因此在此示例中,我们通过为每个问候语设置相同的父级,将一个留言簿的所有问候语归为同一个实体组。这意味着用户将立即看到他们刚刚写下的问候语。但是,您对同一个实体组的写入速率限制为每秒 1 次。在设计实际应用程序时,您需要牢记这一点。通过使用 Memcache 等服务,您可以减少用户在写入后立即查询跨实体组时看不到最新结果的可能性。

使用 datastore.Query 检索存储的问候语

datastore 包提供了一个 Query 类型,用于查询数据存储和迭代结果。

root 处理程序的这个新版本会查询数据存储以获取问候语

func root(w http.ResponseWriter, r *http.Request) {
        c := appengine.NewContext(r)
        // Ancestor queries, as shown here, are strongly consistent with the High
        // Replication Datastore. Queries that span entity groups are eventually
        // consistent. If we omitted the .Ancestor from this query there would be
        // a slight chance that Greeting that had just been written would not
        // show up in a query.
        q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(c)).Order("-Date").Limit(10)
        greetings := make([]Greeting, 0, 10)
        if _, err := q.GetAll(c, &greetings); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
        if err := guestbookTemplate.Execute(w, greetings); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        }
}

首先,函数构建一个 Query 值,该值请求 Greeting 对象,这些对象是根留言簿键的后代,按 Date 降序排列,限制为 10 个对象。

// Create the query.
q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(c)).Order("-Date").Limit(10)

然后,它调用 q.GetAll(c, &greetings),该函数运行查询并将查询结果追加到 greetings 切片中。

greetings := make([]Greeting, 0, 10)
if _, err := q.GetAll(c, &greetings); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
}

最后,guestbookTemplate.Execute 函数渲染一个包含这些问候语的 HTML 页面,并将其写入 http.ResponseWriter。有关模板语言的更多信息,请参阅 text/template 包文档。请注意,我们在此处使用 html/template,这是一个封装 text/template 的包,并且会自动转义 HTML 模板中的内容,从而防止一类脚本注入攻击。

有关数据存储 API 的完整描述,请参阅 Datastore 参考

清除开发服务器数据存储

开发 Web 服务器使用本地版本的数据存储来测试您的应用程序,使用临时文件。数据会一直保留,只要临时文件存在,并且 Web 服务器除非您要求,否则不会重置这些文件。

如果您希望开发服务器在启动前擦除其数据存储,请参阅 开发服务器参考,其中解释了开发服务器的数据存储配置选项。

使用数据存储的完整示例

这是 myapp/hello.go 的一个新版本,它将问候语存储在数据存储中。本页的其余部分将讨论新增的部分。

package guestbook

import (
        "html/template"
        "net/http"
        "time"

        "appengine"
        "appengine/datastore"
        "appengine/user"
)

type Greeting struct {
        Author  string
        Content string
        Date    time.Time
}

func init() {
        http.HandleFunc("/", root)
        http.HandleFunc("/sign", sign)
}

// guestbookKey returns the key used for all guestbook entries.
func guestbookKey(c appengine.Context) *datastore.Key {
        // The string "default_guestbook" here could be varied to have multiple guestbooks.
        return datastore.NewKey(c, "Guestbook", "default_guestbook", 0, nil)
}

func root(w http.ResponseWriter, r *http.Request) {
        c := appengine.NewContext(r)
        // Ancestor queries, as shown here, are strongly consistent with the High
        // Replication Datastore. Queries that span entity groups are eventually
        // consistent. If we omitted the .Ancestor from this query there would be
        // a slight chance that Greeting that had just been written would not
        // show up in a query.
        q := datastore.NewQuery("Greeting").Ancestor(guestbookKey(c)).Order("-Date").Limit(10)
        greetings := make([]Greeting, 0, 10)
        if _, err := q.GetAll(c, &greetings); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
        if err := guestbookTemplate.Execute(w, greetings); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
        }
}

var guestbookTemplate = template.Must(template.New("book").Parse(`
<html>
  <head>
    <title>Go Guestbook</title>
  </head>
  <body>
    {{range .}}
      {{with .Author}}
        <p><b>{{.}}</b> wrote:</p>
      {{else}}
        <p>An anonymous person wrote:</p>
      {{end}}
      <pre>{{.Content}}</pre>
    {{end}}
    <form action="/sign" method="post">
      <div><textarea name="content" rows="3" cols="60"></textarea></div>
      <div><input type="submit" value="Sign Guestbook"></div>
    </form>
  </body>
</html>
`))

func sign(w http.ResponseWriter, r *http.Request) {
        c := appengine.NewContext(r)
        g := Greeting{
                Content: r.FormValue("content"),
                Date:    time.Now(),
        }
        if u := user.Current(c); u != nil {
                g.Author = u.String()
        }
        // We set the same parent key on every Greeting entity to ensure each Greeting
        // is in the same entity group. Queries across the single entity group
        // will be consistent. However, the write rate to a single entity group
        // should be limited to ~1/second.
        key := datastore.NewIncompleteKey(c, "Greeting", guestbookKey(c))
        _, err := datastore.Put(c, key, &g)
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
        http.Redirect(w, r, "/", http.StatusFound)
}

myapp/hello.go 替换为这个,然后重新加载浏览器中的 https://:8080/。发布几条消息以验证消息是否已正确存储和显示。

警告! 在本地执行应用程序中的查询会导致 App Engine 创建或更新 index.yaml。如果 index.yaml 缺失或不完整,当您上传的应用程序执行缺少必要索引的查询时,您将看到索引错误。为避免生产环境中的索引丢失错误,请始终在上传应用程序之前至少在本地测试一次新查询。有关更多信息,请参阅 Go 数据存储索引配置

关于数据存储索引

App Engine 数据存储中的每个查询都是从一个或多个索引计算得出的——映射有序属性值到实体键的表。这就是 App Engine 能够快速提供结果,而不管您的应用程序数据存储的大小。

许多查询可以从内置索引计算得出,但对于更复杂的查询,数据存储需要自定义索引。没有自定义索引,数据存储就无法高效地执行这些查询。例如,我们上面的留言簿应用程序按留言簿条目过滤并按日期排序,使用了祖先查询和排序顺序。这要求在您的应用程序的 index.yaml 文件中指定一个自定义索引。您可以手动编辑此文件,或者,如本页面前面的警告框中所述,您可以通过在本地运行应用程序中的查询来自动处理它。一旦在 index.yaml 中定义了索引,上传您的应用程序也将上传您的自定义索引信息。

您的 index.yaml 文件中的查询定义如下所示

indexes:
- kind: Greeting
  ancestor: yes
  properties:
  - name: Date
    direction: desc

您可以在 Datastore 索引页面 中阅读有关 Datastore 索引的所有信息。您可以在 Go Datastore 索引配置 中阅读有关 index.yaml 文件正确规范的信息。

现在我们拥有了一个可运行的留言簿应用程序,它使用 Google 帐户进行身份验证,允许用户提交消息,并显示其他用户留下的消息。由于 App Engine 会自动处理扩展,因此随着应用程序的普及,我们无需对其进行重构。

上传您的应用程序

您可以使用 Google Cloud 控制台创建和管理 App Engine 应用程序。在为您的应用程序注册了应用程序 ID 后,您将使用 goapp deploy 将其上传到您的网站。

注意: 应用程序 ID 必须以字母开头。一旦您注册了应用程序 ID,您就可以删除它,但删除后不能重新注册同一个应用程序 ID。如果您暂时不想注册 ID,可以跳过接下来的步骤。

注意: 如果您拥有 App Engine Premier 帐户,您可以指定您的新应用程序应位于欧盟而不是美国。没有 Premier 帐户的开发者需要填写此表单为应位于欧盟的应用程序启用账单

在欧盟托管应用程序对于您的用户更接近欧洲而不是美国尤其有用。网络延迟较低,最终用户内容将在欧盟境内存储。您必须在注册应用程序时,在“位置选项”部分单击“编辑”链接来指定此位置;之后无法更改。

注册应用程序

您可以通过此 URL 在开发者控制台中创建和管理 App Engine 应用程序

https://console.developers.google.com/

使用您的 Google 帐户登录 App Engine。如果您没有 Google 帐户,可以 创建一个 Google 帐户,其中包含电子邮件地址和密码。

注意: 您可能已经使用 Google Cloud 控制台创建了一个项目。如果是这样,您无需创建新应用程序。您的项目有一个标题和一个 ID。在接下来的说明中,项目标题和 ID 可以在提到应用程序标题和 ID 的任何地方使用。它们是同一回事。

要创建新应用程序,请单击“创建应用程序”按钮。按照说明注册一个应用程序 ID,这是此应用程序的唯一名称。

如果您选择使用免费的 appspot.com 域名,则该应用程序的完整 URL 为 http://your_app_id.appspot.com/。您也可以为您的应用程序购买顶级域名,或者使用您已注册的域名。

如果您拥有 App Engine Premier 帐户,您可以指定您的新应用程序应位于欧盟而不是美国。这对于您的应用程序用户更接近欧洲而不是美国尤其有用。网络延迟较低,最终用户内容将在欧盟境内存储。您必须在注册应用程序时指定此位置;之后无法更改。单击“位置选项”部分中的“编辑”链接;选择一个位置选项,即美国或欧盟。

编辑 app.yaml 文件,然后将 application: 设置的值从 helloworld 更改为您注册的应用程序 ID。

上传应用程序

要将您完成的应用程序上传到 Google App Engine,请运行命令

goapp deploy myapp/

在提示符处输入您的 Google 用户名和密码。

如果您看到编译错误,请修复源代码并重新运行 goapp deploy;在编译成功之前,它不会启动(或更新)您的应用程序。

检查您的应用程序状态

上传应用程序后,其 Datastore 索引将自动生成。此操作可能需要一些时间,并且直到索引构建完成之前,您的网站的任何访问者都将收到 DatastoreNeedIndexException。您可以通过访问 App Engine 控制台,选择您的应用程序,然后选择 Datastore Indexes 链接来监视此操作的进度。

访问您的应用程序

您现在可以看到您的应用程序正在 App Engine 上运行。如果您设置了免费的 appspot.com 域名,则您的网站 URL 以您的应用程序 ID 开头

http://your_app_id.appspot.com

恭喜!

您已完成本教程。有关此处涵盖的主题的更多信息,请参阅 App Engine 文档的其余部分。

除非另有说明,本页的代码示例根据 Apache 2.0 许可证 授权。

© . All rights reserved.