Go 博客

App Engine SDK 和工作区 (GOPATH)

Andrew Gerrand
2013 年 1 月 9 日

引言

发布 Go 1 时,我们引入了 go tool,并随之引入了工作区的概念。工作区(由 GOPATH 环境变量指定)是一种组织代码的约定,可以简化 Go 软件包的获取、构建和安装。如果您不熟悉工作区,请在继续阅读之前先阅读本文或观看此屏幕录像

直到最近,App Engine SDK 中的工具还不知道工作区。没有工作区,“go get” 命令无法运行,因此应用程序开发者不得不手动安装和更新其应用程序依赖项。这很痛苦。

App Engine SDK 1.7.4 版本完全改变了这一切。dev_appserverappcfg 工具现在都支持工作区。当在本地运行或上传应用程序时,这些工具现在会在 GOPATH 环境变量指定的工作区中搜索依赖项。这意味着您现在可以在构建 App Engine 应用程序时使用“go get”,并且可以在普通 Go 程序和 App Engine 应用程序之间切换,而无需更改您的环境或习惯。

例如,假设您想构建一个使用 OAuth 2.0 与远程服务进行身份验证的应用程序。一个流行的 Go OAuth 2.0 库是 oauth2 软件包,您可以使用此命令将其安装到您的工作区:

go get golang.org/x/oauth2

在编写 App Engine 应用程序时,像在普通 Go 程序中一样导入 oauth 软件包:

import "golang.org/x/oauth2"

现在,无论您是使用 dev_appserver 运行您的应用程序还是使用 appcfg 部署它,工具都会在您的工作区中找到 oauth 软件包。就是这么简单。

混合型独立/App Engine 应用程序

Go App Engine SDK 构建在 Go 的标准 net/http 软件包之上来处理 Web 请求,因此,许多 Go Web 服务器只需稍作修改即可在 App Engine 上运行。例如,godoc 作为独立程序包含在 Go 发布版中,但它也可以作为 App Engine 应用程序运行(godoc 从 App Engine 提供 golang.org 服务)。

但是,如果您可以编写一个既是独立 Web 服务器又是 App Engine 应用程序的程序,那岂不是更好?通过使用 构建约束,您可以做到这一点。

构建约束是行注释,用于确定是否应将文件包含在软件包中。它们最常用于处理各种操作系统或处理器架构的代码。例如,path/filepath 软件包包含文件 symlink.go,该文件指定了一个构建约束,以确保它不会在 Windows 系统(没有符号链接)上构建:

// +build !windows

App Engine SDK 引入了一个新的构建约束项:“appengine”。指定以下内容的文件:

// +build appengine

将由 App Engine SDK 构建,并被 go tool 忽略。相反,指定以下内容的文件:

// +build !appengine

将被 App Engine SDK 忽略,而 go tool 会很乐意构建它们。

goprotobuf 库使用此机制为其编码/解码机制的关键部分提供了两种实现:pointer_unsafe.go 是速度更快的版本,不能在 App Engine 上使用,因为它使用了 unsafe 软件包,而 pointer_reflect.go 是速度较慢的版本,通过使用 reflect 软件包来避免使用 unsafe。

让我们以一个简单的 Go Web 服务器为例,将其转换为混合型应用程序。这是 main.go:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe("localhost:8080", nil)
}

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

使用 go tool 构建它,您将获得一个独立的 Web 服务器可执行文件。

App Engine 基础架构提供了自己的 main 函数,用于运行其等效于 ListenAndServe 的功能。要将 main.go 转换为 App Engine 应用程序,请删除对 ListenAndServe 的调用,并在 init 函数(在 main 之前运行)中注册处理程序。这是 app.go:

package main

import (
    "fmt"
    "net/http"
)

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

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

为了使其成为混合型应用程序,我们需要将其分成一个 App Engine 特定部分、一个独立二进制文件特定部分,以及两个版本共有的部分。在这种情况下,没有 App Engine 特定部分,因此我们只将其分成两个文件:

app.go 指定并注册处理程序函数。它与上面的代码清单完全相同,并且不需要构建约束,因为它应包含在程序的所有版本中。

main.go 运行 Web 服务器。它包含“!appengine”构建约束,因为它只应在构建独立二进制文件时包含。

// +build !appengine

package main

import "net/http"

func main() {
    http.ListenAndServe("localhost:8080", nil)
}

要查看更复杂的混合型应用程序,请查看 present tool

结论

我们希望这些更改能让您更容易地开发具有外部依赖项的应用程序,以及维护包含独立程序和 App Engine 应用程序的代码库。

下一篇文章:并发不是并行
上一篇文章:近期两场 Go 讲座
博客目录